home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 376-400 / disk_388 / dclock / dclock-handler.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  61KB  |  3,128 lines

  1. /* DClock-Handler.c *********************************************************
  2.  *
  3.  *    DClock-Handler.c ------    Dumb clock main handler routines,
  4.  *                display clock data, handle
  5.  *                DisplayBeep, etc.
  6.  *
  7.  *    Author ----------------    Olaf 'Olsen' Barthel, MXM
  8.  *                Brabeckstrasse 35
  9.  *                D-3000 Hannover 71
  10.  *
  11.  *                Federal Republic of Germany
  12.  *
  13.  *    This program truly is in the PUBLIC DOMAIN. Written on a cold
  14.  *    and damp September evening, hoping the next morning would  be
  15.  *    better.
  16.  *
  17.  *    Compiled using Aztec C 5.0a, CygnusEd Professional 2 & ARexx.
  18.  *
  19.  ***************************************************************************/
  20.  
  21.     /* Signal flag aliases. */
  22.  
  23. #define SIG_CLICK    SIGBREAKF_CTRL_C
  24. #define SIG_CLOSE    SIGBREAKF_CTRL_D
  25. #define SIG_TIMER    SIGBREAKF_CTRL_E
  26. #define SIG_TOGGL    SIGBREAKF_CTRL_F
  27. #define SIG_SHAKE    SIGBREAKF_CTRL_D
  28. #define SIG_BENCH    (1 << BenchSig)
  29. #define SIG_WINDO    (1 << Window -> UserPort -> mp_SigBit)
  30. #define SIG_DISPL    (1 << DisplaySig)
  31. #define SIG_SPEECH    (1 << SpeechSig)
  32.  
  33.     /* Prototypes. */
  34.  
  35. struct InputEvent *    EventHandler(struct InputEvent *Event);
  36. VOID             Click(VOID);
  37. VOID            FlushSound(VOID);
  38. UBYTE            InitSound(VOID);
  39. UBYTE            InitHandler(VOID);
  40. VOID            FlushHandler(VOID);
  41. VOID            ModifiedCloseWBench(struct Screen *);
  42. VOID            AudioBeep(VOID);
  43. VOID            VideoBeep(struct Screen *Screen,BYTE Perform);
  44. VOID            ModifiedDisplayBeep(struct Screen *Screen);
  45. VOID            PrintIt(STRPTR TimeBuff);
  46. VOID            Ring(LONG Tea);
  47. VOID            ShowTime(UBYTE ReallyDoIt,UBYTE Force);
  48. ULONG            MaxMemSize(ULONG MemType);
  49. struct Screen *        FindTheBench(VOID);
  50. VOID            ShutDown(LONG HandShake);
  51. LONG            RangeRand(LONG);
  52. struct Process *    CreateFuncProc(char *Name,LONG Priority,APTR InitCode,ULONG StackSize);
  53. UBYTE            MaxFontWidth(VOID);
  54. VOID            DeleteDummyRPort(VOID);
  55. BYTE            CreateDummyRPort(VOID);
  56. VOID            Format(VOID *,char *,...);
  57. LONG            PlayChime(VOID);
  58. VOID            StopChime(VOID);
  59. LONG            FindChunk(ULONG ChunkName,BPTR FileHandle);
  60. LONG            LoadChimeSound(char *Name);
  61.  
  62. extern VOID        NewDisplayBeep(VOID);
  63. extern VOID        NewCloseWBench(VOID);
  64.  
  65.     /* The ARexx server routines. */
  66.  
  67. STRPTR            CheckDClockStatus(STRPTR);
  68. VOID            RexxServer(VOID);
  69.  
  70.     /* Interrupt register saving functions. */
  71.  
  72. VOID            int_start(VOID);
  73. VOID            int_end(VOID);
  74.  
  75.     /* System specific functions. */
  76.  
  77. VOID            setenv(char *,char *);
  78. LONG            _main(VOID);
  79.  
  80.     /* The speech server. */
  81.  
  82. VOID            SpeechServer(VOID);
  83.  
  84.     /* The magic pragmas. */
  85.  
  86. #pragma regcall(EventHandler(a0))
  87. #pragma regcall(ModifiedDisplayBeep(a0))
  88.  
  89.     /* Some global data. */
  90.  
  91. struct IntuitionBase    *IntuitionBase;
  92. struct GfxBase        *GfxBase;
  93. extern struct ExecBase    *SysBase;
  94. struct Library        *DiskfontBase;
  95. struct RexxHostBase    *RexxHostBase;
  96. struct Window        *Window;
  97. struct Process        *HandlerProcess;
  98. struct Process        *RexxProcess;
  99. struct MsgPort        *RexxTaskPort;
  100. struct Process        *SpeechProcess;
  101. struct DSeg        *DSeg;
  102. BYTE             NewKick = FALSE;
  103. BYTE             Printed = FALSE;
  104. LONG             BenchSig = -1;
  105. LONG             DisplaySig = -1;
  106. LONG             SpeechSig = -1;
  107.  
  108.     /* Our current version tag. */
  109.  
  110. const char *VersionTag = "$VER: DClock-Handler 1.27 (26 Jul 1990)\n\r";
  111.  
  112.     /* Online time. */
  113.  
  114. UBYTE OnlineHours = 0,OnlineMinutes = 0,OnlineSeconds = 0;
  115.  
  116.     /* The chime data. */
  117.  
  118. struct IOAudio        *ChimeAudioBlock;
  119. struct MsgPort        *ChimeReplyPort;
  120. UBYTE            *ChimeWaveMap;
  121.  
  122.     /* Our dummy RastPort. */
  123.  
  124. struct BitMap        *DummyMap;
  125. struct RastPort        *DummyRPort;
  126. struct TextFont        *DummyFont;
  127. BYTE             DummyDepth;
  128.  
  129.     /* Static DClock window size and location. */
  130.  
  131. LONG LeftEdge = 409,TopEdge = 1,Width = 176,Height = 8;
  132.  
  133.     /* Audio.device control structures. */
  134.  
  135. struct MsgPort        *AudioPort;
  136. struct IOAudio        *Audio;
  137.  
  138.     /* Input.device control structures. */
  139.  
  140. struct MsgPort        *InputDevPort;
  141. struct IOStdReq        *InputRequestBlock;
  142.  
  143.     /* The interrupt handler control. */
  144.  
  145. struct Interrupt    *InputHandler;
  146.  
  147.     /* Console.device control structures. */
  148.  
  149. struct Device        *ConsoleDevice;
  150. struct IOStdReq        *ConStdReq;
  151. struct InputEvent    *CopyEvent;
  152.  
  153.     /* The library offset pointers (old ones). */
  154.  
  155. VOID *OldDisplayBeep;
  156. VOID *OldCloseWBench;
  157.  
  158.     /* External data. */
  159.  
  160. extern ULONG        SoundData[67];
  161. extern ULONG        RingData[1831];
  162. extern USHORT        ClockMap[456];
  163. extern struct TextAttr    DefaultFont;
  164. extern struct IntuiText    TimeString;
  165. extern struct NewWindow    NewWindow;
  166. extern UBYTE        AnyChannel[4];
  167. extern UBYTE        SquareWave[4];
  168. extern struct Image    ClockImage;
  169. extern struct Gadget    ClockGadget[2];
  170. extern struct NewWindow    NewClockWindow;
  171. extern struct IntuiText    ClockTxt[4];
  172.  
  173.     /* EventHandler(Event) :
  174.      *
  175.      *    This is the main interface to the handler
  176.      *    routine.
  177.      */
  178.  
  179. struct InputEvent *
  180. EventHandler(struct InputEvent *Event)
  181. {
  182.     int_start();
  183.  
  184.         /* User wants to toggle text and memory mode. */
  185.  
  186.     if(Event -> ie_Class == IECLASS_RAWKEY && Event -> ie_Code == 0x5F && Event -> ie_Qualifier == RIGHT_AMIGA)
  187.     {
  188.         Event -> ie_Class = IECLASS_NULL;
  189.  
  190.         if(DSeg -> Child)
  191.             Signal(DSeg -> Child,SIG_TOGGL);
  192.     }
  193.  
  194.         /* User wants to know the time? */
  195.  
  196.     if(Event -> ie_Class == IECLASS_RAWKEY && Event -> ie_Code == 0x5F && Event -> ie_Qualifier == RIGHT_ALT)
  197.     {
  198.         Event -> ie_Class = IECLASS_NULL;
  199.  
  200.         if(DSeg -> Child)
  201.             Signal(DSeg -> Child,SIG_SPEECH);
  202.     }
  203.  
  204.         /* Modify the time display size? */
  205.  
  206.     if(Event -> ie_Class == IECLASS_RAWKEY && Event -> ie_Code == 0x46 && Event -> ie_Qualifier == RIGHT_AMIGA)
  207.     {
  208.         Event -> ie_Class = IECLASS_NULL;
  209.  
  210.         if(DSeg -> Child)
  211.             Signal(DSeg -> Child,SIG_DISPL);
  212.     }
  213.  
  214.         /* If we can use it, initialize the copyevent and signal
  215.          * the main process to produce a click.
  216.          */
  217.  
  218.     if(Event -> ie_Class == IECLASS_RAWKEY && !(Event -> ie_Code & IECODE_UP_PREFIX) && DSeg -> Click)
  219.     {
  220.         CopyEvent -> ie_Class        = Event -> ie_Class;
  221.         CopyEvent -> ie_Code        = Event -> ie_Code;
  222.         CopyEvent -> ie_Qualifier    = Event -> ie_Qualifier;
  223.  
  224.             /* This is safe from interrupt code, or
  225.              * at least should be.
  226.              */
  227.  
  228.         if(DSeg -> Child)
  229.             Signal(DSeg -> Child,SIG_CLICK);
  230.     }
  231.  
  232.         /* I had a lot of trouble getting DClock-Handler to
  233.          * work with timer.device. Well, the only thing
  234.          * happening in time were system crashes, so I
  235.          * decided to use the timer entries of the
  236.          * InputEvent structures.
  237.          */
  238.  
  239.     if(Event -> ie_TimeStamp . tv_secs != DSeg -> LastSecs)
  240.     {
  241.         DSeg -> LastSecs = Event -> ie_TimeStamp . tv_secs;
  242.  
  243.         if(DSeg -> Child)
  244.             Signal(DSeg -> Child,SIG_TIMER);
  245.  
  246.         /* Each second we take a look at CIA B, port A
  247.          * to find out if a carrier signal is currently
  248.          * present at the serial port. Note that the port
  249.          * bits are low-active.
  250.          */
  251.  
  252.         if(!(ciab . ciapra & CIAF_COMCD))
  253.         {
  254.                 /* Are we online? */
  255.  
  256.             if(!DSeg -> Online)
  257.             {
  258.                 /* Reset counters. */
  259.  
  260.                 OnlineHours = OnlineMinutes = OnlineSeconds = 0;
  261.                 DSeg -> Online = TRUE;
  262.             }
  263.  
  264.                 /* Increment time counter. */
  265.  
  266.             if(++OnlineSeconds == 60)
  267.             {
  268.                 OnlineSeconds = 0;
  269.  
  270.                 if(++OnlineMinutes == 60)
  271.                 {
  272.                     OnlineMinutes = 0;
  273.  
  274.                     ++OnlineHours;
  275.                 }
  276.             }
  277.         }
  278.         else
  279.             DSeg -> Online = FALSE;
  280.     }
  281.  
  282.     int_end();
  283.  
  284.     return(Event);
  285. }
  286.  
  287.     /* Click() :
  288.      *
  289.      *    Produces the click (or what did you expect?).
  290.      */
  291.  
  292. VOID
  293. Click()
  294. {
  295.     char    PrimaryBuffer[11];    /* Rawkey conversion buffer. */
  296.     SHORT    i;
  297.  
  298.         /* Erase the buffer. */
  299.  
  300.     for(i = 0 ; i < 11 ; i++)
  301.         PrimaryBuffer[i] = 0;
  302.  
  303.         /* Convert the input event according to the
  304.          * current keymap settings.
  305.          */
  306.  
  307.     RawKeyConvert(CopyEvent,PrimaryBuffer,10,NULL);
  308.  
  309.         /* If it didn't produce a sensible result,
  310.          * don't click.
  311.          */
  312.  
  313.     if(!PrimaryBuffer[0])
  314.         return;
  315.  
  316.     Audio -> ioa_Volume = DSeg -> ClickVolume;
  317.  
  318.         /* Let it click. */
  319.  
  320. Tick:    if(CheckIO(Audio))
  321.         BeginIO(Audio);
  322. }
  323.  
  324.     /* FlushSound() :
  325.      *
  326.      *    Send the sound control data to NIL:
  327.      */
  328.  
  329. VOID
  330. FlushSound()
  331. {
  332.     if(Audio)
  333.     {
  334.             /* Audio device still open? */
  335.  
  336.         if(Audio -> ioa_Request . io_Device)
  337.         {
  338.             if(!CheckIO(Audio))
  339.                 WaitIO(Audio);
  340.  
  341.                 /* Free the channel(s). */
  342.  
  343.             CloseDevice(Audio);
  344.         }
  345.  
  346.             /* Free the audio control block. */
  347.  
  348.         FreeMem(Audio,sizeof(struct IOAudio));
  349.     }
  350.  
  351.         /* Delete the replyport. */
  352.  
  353.     if(AudioPort)
  354.         DeletePort(AudioPort);
  355. }
  356.  
  357.     /* InitSound() :
  358.      *
  359.      *    Sets up the audio control structures.
  360.      */
  361.  
  362. UBYTE
  363. InitSound()
  364. {
  365.     if(!(Audio = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR)))
  366.         return(FALSE);
  367.  
  368.     if(!(AudioPort = (struct MsgPort *)CreatePort(NULL,0)))
  369.         return(FALSE);
  370.  
  371.         /* Open the channel. */
  372.  
  373.     if(OpenDevice(AUDIONAME,0,Audio,0))
  374.         return(FALSE);
  375.  
  376.         /* Try to allocate a vacant channel. */
  377.  
  378.     Audio -> ioa_Request . io_Command            = ADCMD_ALLOCATE;
  379.     Audio -> ioa_Request . io_Flags                = ADIOF_NOWAIT;
  380.     Audio -> ioa_Request . io_Message . mn_Node . ln_Pri    = 100;
  381.     Audio -> ioa_Request . io_Message . mn_ReplyPort    = AudioPort;
  382.     Audio -> ioa_Data                    = AnyChannel;
  383.     Audio -> ioa_Length                    = 4;
  384.  
  385.         /* Try the allocation. */
  386.  
  387.     BeginIO(Audio);
  388.  
  389.         /* Did it return an error? */
  390.  
  391.     if(WaitIO(Audio))
  392.         return(FALSE);
  393.  
  394.         /* Prepare it for the click. */
  395.  
  396.     Audio -> ioa_Request . io_Command    = CMD_WRITE;
  397.     Audio -> ioa_Request . io_Flags        = ADIOF_PERVOL | ADIOF_NOWAIT;
  398.     Audio -> ioa_Period            = 180;
  399.     Audio -> ioa_Volume            = 0;
  400.     Audio -> ioa_Length            = 270;
  401.     Audio -> ioa_Data            = (UBYTE *)SoundData;
  402.     Audio -> ioa_Cycles            = 1;
  403.  
  404.         /* Click once. */
  405.  
  406.     BeginIO(Audio);
  407.     WaitIO(Audio);
  408.  
  409.     return(TRUE);
  410. }
  411.  
  412.     /* InitHandler() :
  413.      *
  414.      *    Open the console.device for keymap translation
  415.      *    and add the input.device handler.
  416.      */
  417.  
  418. UBYTE
  419. InitHandler()
  420. {
  421.     if(!(ConStdReq = (struct IOStdReq *)AllocMem(sizeof(struct IOStdReq),MEMF_PUBLIC | MEMF_CLEAR)))
  422.         return(FALSE);
  423.  
  424.     if(OpenDevice("console.device",-1,ConStdReq,0))
  425.         return(FALSE);
  426.  
  427.     if(!(InputDevPort = (struct MsgPort *)CreatePort(NULL,0)))
  428.         return(FALSE);
  429.  
  430.     if(!(InputRequestBlock = (struct IOStdReq *)CreateStdIO(InputDevPort)))
  431.         return(FALSE);
  432.  
  433.     if(OpenDevice("input.device",0,InputRequestBlock,0))
  434.         return(FALSE);
  435.  
  436.     if(!(InputHandler = (struct Interrupt *)AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC | MEMF_CLEAR)))
  437.         return(FALSE);
  438.  
  439.     if(!(CopyEvent = (struct InputEvent *)AllocMem(sizeof(struct InputEvent),MEMF_PUBLIC | MEMF_CLEAR)))
  440.         return(FALSE);
  441.  
  442.     InputHandler -> is_Node . ln_Name    = "DClock-Handler";
  443.     InputHandler -> is_Node . ln_Pri    = 51;
  444.     InputHandler -> is_Code            = (VOID *)EventHandler;
  445.  
  446.     InputRequestBlock -> io_Command        = IND_ADDHANDLER;
  447.     InputRequestBlock -> io_Data        = (APTR)InputHandler;
  448.  
  449.     DoIO(InputRequestBlock);
  450.  
  451.     ConsoleDevice = ConStdReq -> io_Device;
  452.  
  453.     return(TRUE);
  454. }
  455.  
  456.     /* FlushHandler() :
  457.      *
  458.      *    Closes the console.device and removes the
  459.      *    input.device handler from the chain.
  460.      */
  461.  
  462. VOID
  463. FlushHandler()
  464. {
  465.     if(ConsoleDevice)
  466.         CloseDevice(ConStdReq);
  467.  
  468.     if(InputRequestBlock)
  469.     {
  470.         if(InputRequestBlock -> io_Device)
  471.         {
  472.             InputRequestBlock -> io_Command    = IND_REMHANDLER;
  473.             InputRequestBlock -> io_Data    = (APTR)InputHandler;
  474.  
  475.             DoIO(InputRequestBlock);
  476.  
  477.             CloseDevice(InputRequestBlock);
  478.         }
  479.  
  480.         DeleteStdIO(InputRequestBlock);
  481.     }
  482.  
  483.     if(ConStdReq)
  484.         FreeMem(ConStdReq,sizeof(struct IOStdReq));
  485.  
  486.     if(CopyEvent)
  487.         FreeMem(CopyEvent,sizeof(struct InputEvent));
  488.  
  489.     if(InputHandler)
  490.         FreeMem(InputHandler,sizeof(struct Interrupt));
  491.  
  492.     if(InputDevPort)
  493.         DeletePort(InputDevPort);
  494. }
  495.  
  496.     /* ModifiedCloseWBench():
  497.      *
  498.      *    Tells DClock to close its window before the
  499.      *    Workbench screen gets closed.
  500.      */
  501.  
  502. VOID
  503. ModifiedCloseWBench()
  504. {
  505.     if(Window)
  506.     {
  507.         struct Task    *ThisTask = SysBase -> ThisTask;
  508.         BYTE         CurrentPri;
  509.  
  510.             /* Shut down... */
  511.  
  512.         Signal(DSeg -> Child,SIG_BENCH);
  513.  
  514.             /* Careful - rather rude window close check,
  515.              * don't waste too much time in the loop.
  516.              */
  517.  
  518.         CurrentPri = SetTaskPri(ThisTask,0);
  519.  
  520.         while(Window);
  521.  
  522.         SetTaskPri(ThisTask,CurrentPri);
  523.     }
  524. }
  525.  
  526. VOID
  527. AudioBeep()
  528. {
  529.     struct IOAudio    *AudioBlock;
  530.     struct MsgPort    *ReplyPort;
  531.  
  532.         /* Allocate some driver memory. */
  533.  
  534.     if(AudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR))
  535.     {
  536.             /* Time for a replyport? */
  537.  
  538.         if(ReplyPort = (struct MsgPort *)CreatePort(NULL,0))
  539.         {
  540.             AudioBlock -> ioa_Request . io_Message . mn_ReplyPort = ReplyPort;
  541.  
  542.             if(!OpenDevice(AUDIONAME,0,AudioBlock,0))
  543.             {
  544.                     /* Set up initial driver data. */
  545.  
  546.                 AudioBlock -> ioa_Request . io_Command                = ADCMD_ALLOCATE;
  547.                 AudioBlock -> ioa_Request . io_Message . mn_Node . ln_Pri    = 90;
  548.                 AudioBlock -> ioa_Data                        = &AnyChannel[0];
  549.                 AudioBlock -> ioa_Length                    = 4;
  550.  
  551.                 if(!DoIO(AudioBlock))
  552.                 {
  553.                     AudioBlock -> ioa_Request . io_Command    = CMD_WRITE;
  554.                     AudioBlock -> ioa_Request . io_Flags    = ADIOF_PERVOL;
  555.                     AudioBlock -> ioa_Period        = 447;
  556.                     AudioBlock -> ioa_Volume        = 64 / 2;
  557.                     AudioBlock -> ioa_Cycles        = 150;
  558.                     AudioBlock -> ioa_Data            = &SquareWave[0];
  559.                     AudioBlock -> ioa_Length        = 4;
  560.  
  561.                         /* Beeep! */
  562.  
  563.                     BeginIO(AudioBlock);
  564.                     WaitIO(AudioBlock);
  565.                 }
  566.  
  567.                 CloseDevice(AudioBlock);
  568.             }
  569.  
  570.             DeletePort(ReplyPort);
  571.         }
  572.  
  573.         FreeMem(AudioBlock,sizeof(struct IOAudio));
  574.     }
  575. }
  576.  
  577.     /* VideoBeep(Screen,Perform):
  578.      *
  579.      *    Handles the visual part of the DisplayBeep,
  580.      *    flashes a particular screen or restores its
  581.      *    original colour (well, hope so).
  582.      */
  583.  
  584. VOID
  585. VideoBeep(struct Screen *Screen,BYTE Perform)
  586. {
  587.     UBYTE R,G,B;
  588.  
  589.         /* Beep this screen? */
  590.  
  591.     if(Perform)
  592.     {
  593.             /* Is it already beeping? */
  594.  
  595.         if(!(Screen -> Flags & BEEPING))
  596.         {
  597.                 /* This one's beeping. */
  598.  
  599.             Screen -> Flags |= BEEPING;
  600.  
  601.                 /* Don't forget this one. */
  602.  
  603.             Screen -> SaveColor0 = GetRGB4(Screen -> ViewPort . ColorMap,0);
  604.  
  605.                 /* Reverse the colour. */
  606.  
  607.             R = ((Screen -> SaveColor0 >> 8) & 0xF) ^ 0xF;
  608.             G = ((Screen -> SaveColor0 >> 4) & 0xF) ^ 0xF;
  609.             B = ((Screen -> SaveColor0     ) & 0xF) ^ 0xF;
  610.  
  611.                 /* Set it. */
  612.  
  613.             SetRGB4(&Screen -> ViewPort,0,R,G,B);
  614.         }
  615.     }
  616.     else
  617.     {
  618.             /* Is this one beeping? */
  619.  
  620.         if(Screen -> Flags & BEEPING)
  621.         {
  622.                 /* This one isn't beeping any longer. */
  623.  
  624.             Screen -> Flags &= ~BEEPING;
  625.  
  626.                 /* Restore the saved colour. */
  627.  
  628.             R = ((Screen -> SaveColor0 >> 8) & 0xF);
  629.             G = ((Screen -> SaveColor0 >> 4) & 0xF);
  630.             B = ((Screen -> SaveColor0     ) & 0xF);
  631.  
  632.             SetRGB4(&Screen -> ViewPort,0,R,G,B);
  633.         }
  634.     }
  635. }
  636.  
  637.     /* ModifiedDisplayBeep(Screen):
  638.      *
  639.      *    Magic replacement for usual DisplayBeep()
  640.      *    function.
  641.      */
  642.  
  643. VOID
  644. ModifiedDisplayBeep(struct Screen *Screen)
  645. {
  646.         /* Flash a particular screen. */
  647.  
  648.     if(Screen)
  649.     {
  650.         VideoBeep(Screen,TRUE);
  651.  
  652.         if(DSeg -> Beep)
  653.             AudioBeep();
  654.  
  655.         VideoBeep(Screen,FALSE);
  656.     }
  657.     else
  658.     {
  659.             /* Flash all screens. */
  660.  
  661.         ULONG IntuiLock;
  662.  
  663.             /* Where's the first one? Has anybody
  664.              * used the LockIBase() function so
  665.              * far (save me)?
  666.              */
  667.  
  668.         IntuiLock = LockIBase(NULL);
  669.  
  670.         Screen = IntuitionBase -> FirstScreen;
  671.  
  672.             /* Walk through the screens flashing them all. */
  673.  
  674.         do
  675.             VideoBeep(Screen,TRUE);
  676.         while(Screen = Screen -> NextScreen);
  677.  
  678.         UnlockIBase(IntuiLock);
  679.  
  680.             /* Let it resound. */
  681.  
  682.         if(DSeg -> Beep)
  683.             AudioBeep();
  684.  
  685.             /* Again: where's the first screen? */
  686.  
  687.         IntuiLock = LockIBase(NULL);
  688.  
  689.         Screen = IntuitionBase -> FirstScreen;
  690.  
  691.         do
  692.             VideoBeep(Screen,FALSE);
  693.         while(Screen = Screen -> NextScreen);
  694.  
  695.         UnlockIBase(IntuiLock);
  696.     }
  697. }
  698.  
  699.     /* PrintIt(TimeBuff,CharOffset):
  700.      *
  701.      *    Prints the formatted string into the Workbench title bar.
  702.      */
  703.  
  704. VOID
  705. PrintIt(STRPTR TimeBuff)
  706. {
  707.     BYTE Length = strlen((char *)TimeBuff);
  708.     BYTE Offset = Width - TextLength(DummyRPort,TimeBuff,Length);
  709.  
  710.     SetAPen(DummyRPort,DSeg -> TextColour);
  711.     SetBPen(DummyRPort,DSeg -> BackColour);
  712.  
  713.     SetRast(DummyRPort,1);
  714.  
  715.     Move(DummyRPort,(Offset >= 0 ? Offset : 0),DummyRPort -> Font -> tf_Baseline);
  716.     Text(DummyRPort,TimeBuff,Length);
  717. }
  718.  
  719.     /* Ring():
  720.      *
  721.      *    This one rings the bell of the alarm clock.
  722.      */
  723.  
  724. VOID
  725. Ring(LONG Tea)
  726. {
  727.     struct IOAudio        *AudioBlock;
  728.     struct MsgPort        *ReplyPort;
  729.  
  730.     struct Screen         PublicScreen;
  731.     struct Screen        *FirstOne;
  732.     struct Window        *ClockWindow;
  733.     struct IntuiMessage    *Massage;
  734.  
  735.     ULONG             Class;
  736.     USHORT             Code;
  737.     struct Gadget        *ID;
  738.  
  739.     ULONG             IntuiLock;
  740.     struct View        *ViewLord;
  741.  
  742.     SHORT             DyOffset,PlusY;
  743.     LONG             TimeOut;
  744.  
  745.         /* Remember initial first screen. */
  746.  
  747.     IntuiLock = LockIBase(NULL);
  748.     FirstOne = IntuitionBase -> FirstScreen;
  749.     UnlockIBase(IntuiLock);
  750.  
  751.         /* Knockin' on heaven's door... */
  752.  
  753.     OpenWorkBench();
  754.  
  755.         /* Center the clock window. */
  756.  
  757.     GetScreenData(&PublicScreen,sizeof(struct Screen),WBENCHSCREEN,NULL);
  758.  
  759.     NewClockWindow . LeftEdge    = (PublicScreen . Width - NewClockWindow . Width) / 2;
  760.     NewClockWindow . TopEdge    = (PublicScreen . Height - NewClockWindow . Height) / 2;
  761.  
  762.         /* Open it and paint the background. */
  763.  
  764.     if(!(ClockWindow = (struct Window *)OpenWindow(&NewClockWindow)))
  765.     {
  766.         DisplayBeep(NULL);
  767.         goto Quit;
  768.     }
  769.  
  770.     if(NewKick)
  771.         SetAPen(ClockWindow -> RPort,2);
  772.     else
  773.         SetAPen(ClockWindow -> RPort,1);
  774.  
  775.     RectFill(ClockWindow -> RPort,2,1,ClockWindow -> Width - 3,ClockWindow -> Height - 2);
  776.  
  777.     RefreshGadgets(&ClockGadget[0],ClockWindow,NULL);
  778.  
  779.         /* Adjust the contents of the alarm time string. */
  780.  
  781.     if(Tea)
  782.         Format(ClockTxt[3] . IText,"Alarm time » %2ld:%02ld:%02ld «",DSeg -> AlarmHour,DSeg -> AlarmMinute,DSeg -> AlarmSecond);
  783.     else
  784.         strcpy((char *)ClockTxt[3] . IText,"Countdown elapsed!");
  785.  
  786.     PrintIText(ClockWindow -> RPort,&ClockTxt[0],0,0);
  787.  
  788.         /* Allocate some driver memory. */
  789.  
  790.     if(AudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR))
  791.     {
  792.             /* Time for a replyport? */
  793.  
  794.         if(ReplyPort = (struct MsgPort *)CreatePort(NULL,0))
  795.         {
  796.                 /* Set up initial driver data. */
  797.  
  798.             AudioBlock -> ioa_Data                    = &AnyChannel[0];
  799.             AudioBlock -> ioa_Length                = 4;
  800.             AudioBlock -> ioa_Request . io_Message . mn_ReplyPort    = ReplyPort;
  801.             AudioBlock -> ioa_Request . io_Message . mn_Node.ln_Pri    = 80;
  802.  
  803.                 /* Allocate the channels on the fly. */
  804.  
  805.             if(!OpenDevice(AUDIONAME,0,AudioBlock,0))
  806.             {
  807.                 AudioBlock -> ioa_Request . io_Command    = CMD_WRITE;
  808.                 AudioBlock -> ioa_Request . io_Flags    = ADIOF_PERVOL;
  809.                 AudioBlock -> ioa_Period        = 308;
  810.                 AudioBlock -> ioa_Volume        = 64;
  811.                 AudioBlock -> ioa_Cycles        = 1;
  812.                 AudioBlock -> ioa_Data            = (UBYTE *)&RingData[0];
  813.                 AudioBlock -> ioa_Length        = 7326;
  814.  
  815.                 IntuiLock = LockIBase(NULL);
  816.                 ViewLord = &IntuitionBase -> ViewLord;
  817.                 UnlockIBase(IntuiLock);
  818.  
  819.                 DyOffset = ViewLord -> DyOffset;
  820.  
  821.                     /* Ring! */
  822.  
  823.                 BeginIO(AudioBlock);
  824.  
  825.                 WBenchToFront();
  826.  
  827.                 TimeOut = 0;
  828.  
  829.                     /* Ring until somebody clicked our window
  830.                      * or a timeout occurs.
  831.                      */
  832.  
  833.                 FOREVER
  834.                 {
  835.                         /* Cycles already finished. */
  836.  
  837.                     if(CheckIO(AudioBlock))
  838.                         BeginIO(AudioBlock);
  839.  
  840.                     Class = Code = NULL;
  841.  
  842.                     if(Massage = GetMsg(ClockWindow -> UserPort))
  843.                     {
  844.                         Class = Massage -> Class;
  845.                         Code  = Massage -> Code;
  846.                         ID    = (struct Gadget *)Massage -> IAddress;
  847.  
  848.                         ReplyMsg(Massage);
  849.  
  850.                         if((Class == GADGETUP && !ID -> GadgetID) || Class == VANILLAKEY)
  851.                             break;
  852.                     }
  853.  
  854.                     PlusY = 1 - RangeRand(3);
  855.  
  856.                         /* Make the view vibrate. */
  857.  
  858.                     if(DyOffset + PlusY >= 0)
  859.                         ViewLord -> DyOffset = DyOffset + PlusY;
  860.  
  861.                     RethinkDisplay();
  862.  
  863.                         /* Wait a tick. */
  864.  
  865.                     Delay(1);
  866.  
  867.                         /* Now for the timeout... */
  868.  
  869.                     if((TimeOut++) >= (TICKS_PER_SECOND * 30))
  870.                         break;
  871.                 }
  872.  
  873.                 ViewLord -> DyOffset = DyOffset;
  874.                 RethinkDisplay();
  875.  
  876.                     /* Still ringing? */
  877.  
  878.                 if(!CheckIO(AudioBlock))
  879.                     WaitIO(AudioBlock);
  880.  
  881.                     /* Tick! */
  882.  
  883.                 CloseDevice(AudioBlock);
  884.             }
  885.  
  886.             DeletePort(ReplyPort);
  887.         }
  888.  
  889.         FreeMem(AudioBlock,sizeof(struct IOAudio));
  890.     }
  891.  
  892.         /* Bring original first screen to the front again. */
  893.  
  894. Quit:    if(FirstOne != ClockWindow -> WScreen)
  895.         ScreenToBack(ClockWindow -> WScreen);
  896.  
  897.     if(ClockWindow)
  898.         CloseWindow(ClockWindow);
  899. }
  900.  
  901.     /* ShowTime(ReallyDoIt,Force):
  902.      *
  903.      *    Yes, it's Showtime! This one compiles the date/timestring
  904.      *    and prints it.
  905.      */
  906.  
  907. VOID
  908. ShowTime(UBYTE ReallyDoIt,UBYTE Force)
  909. {
  910.     UBYTE TempBuff[30];
  911.  
  912.     static char *Months[12] =
  913.     {
  914.         "Jan","Feb","Mar",
  915.         "Apr","May","Jun",
  916.         "Jul","Aug","Sep",
  917.         "Oct","Nov","Dec"
  918.     };
  919.  
  920.     static char *Days[7] =
  921.     {
  922.         "Sun",    /* Note: these have to appear right in this order. */
  923.         "Mon",
  924.         "Tue",
  925.         "Wed",
  926.         "Thu",
  927.         "Fri",
  928.         "Sat"
  929.     };
  930.  
  931.     static char *LongDays[7] =
  932.     {
  933.         "Sunday",
  934.         "Monday",
  935.         "Tuesday",
  936.         "Wednesday",
  937.         "Thursday",
  938.         "Friday",
  939.         "Saturday"
  940.     };
  941.  
  942.     static char DateBuff[15],TimeBuff[10];
  943.  
  944.     static UBYTE LastState = 2;
  945.  
  946.     static SHORT MonthVectors[14] =
  947.     {
  948.         -1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
  949.     };
  950.  
  951.     LONG JulianDate, Day0, Day1, Day2, Day3;
  952.     LONG Year, Month, Day;
  953.  
  954.     struct DateStamp Date;
  955.  
  956.     DateStamp(&Date);
  957.  
  958.     JulianDate = Date . ds_Days + DDELTA;
  959.  
  960.     Year = (JulianDate / 146097) * 400;
  961.  
  962.     Day0 = Day1 = JulianDate %= 146097;
  963.  
  964.     Year += (JulianDate / 36524) * 100;
  965.  
  966.     Day2 = Day1 %= 36524;
  967.  
  968.     Year += (Day2 / 1461) * 4;
  969.  
  970.     Day3 = Day1 %= 1461;
  971.  
  972.     Year += Day3 / 365;
  973.  
  974.     Month = 1 + (Day1 %= 365);
  975.  
  976.     Day = Month % 30;
  977.  
  978.     Month /= 30;
  979.  
  980.     if((Day3 >= 59 && Day0 < 59) || (Day3 < 59 && (Day2 >= 59 || Day0 < 59)))
  981.         Day1++;
  982.  
  983.     if(Day1 > MonthVectors[1 + Month])
  984.         Month++;
  985.  
  986.     Day = Day1 - MonthVectors[Month];
  987.  
  988.     DSeg -> CurrentTime . Year    = Year;
  989.     DSeg -> CurrentTime . Month    = Month;
  990.     DSeg -> CurrentTime . Day    = Day;
  991.     DSeg -> CurrentTime . Weekday    = Date . ds_Days % DAYS_PER_WEEK;
  992.  
  993.     DSeg -> CurrentTime . Hour    = Date . ds_Minute / MINS_PER_HOUR;
  994.     DSeg -> CurrentTime . Minute    = Date . ds_Minute % MINS_PER_HOUR;
  995.     DSeg -> CurrentTime . Second    = Date . ds_Tick   / TICS_PER_SEC;
  996.  
  997.         /* Did we need a change? */
  998.  
  999.     if(LastState != DSeg -> Seconds)
  1000.     {
  1001.         LastState = DSeg -> Seconds;
  1002.         Force = TRUE;
  1003.     }
  1004.  
  1005.     if(ReallyDoIt)
  1006.     {
  1007.         if(DSeg -> Seconds)
  1008.         {
  1009.             Format(TempBuff,"%s %02ld-%s-%02ld %02ld:%02ld:%02ld",
  1010.                 Days[DSeg -> CurrentTime . Weekday],
  1011.                 DSeg -> CurrentTime . Day,
  1012.                 Months[DSeg -> CurrentTime . Month - 1],
  1013.                 DSeg -> CurrentTime . Year % 100,
  1014.                 DSeg -> CurrentTime . Hour,
  1015.                 DSeg -> CurrentTime . Minute,
  1016.                 DSeg -> CurrentTime . Second);
  1017.  
  1018.             PrintIt(TempBuff);
  1019.         }
  1020.         else
  1021.         {
  1022.             if(!(Date . ds_Tick / TICKS_PER_SECOND) || Force)
  1023.             {
  1024.                 Format(TempBuff,"%s %02ld-%s-%02ld %02ld:%02ld",
  1025.                     Days[DSeg -> CurrentTime . Weekday],
  1026.                     DSeg -> CurrentTime . Day,
  1027.                     Months[DSeg -> CurrentTime . Month - 1],
  1028.                     DSeg -> CurrentTime . Year % 100,
  1029.                     DSeg -> CurrentTime . Hour,
  1030.                     DSeg -> CurrentTime . Minute);
  1031.  
  1032.                 SetRast(DummyRPort,1);
  1033.  
  1034.                 PrintIt(TempBuff);
  1035.             }
  1036.         }
  1037.     }
  1038.  
  1039.     if((!DSeg -> CurrentTime . Second || !Printed) && DSeg -> SetEnv)
  1040.     {
  1041.         Printed = TRUE;
  1042.  
  1043.         Format(DateBuff,"%02ld-%s-%02ld",Day,Months[DSeg -> CurrentTime . Month - 1],DSeg -> CurrentTime . Year % 100);
  1044.         Format(TimeBuff,"%02ld:%02ld",DSeg -> CurrentTime . Hour,DSeg -> CurrentTime . Minute);
  1045.  
  1046.         setenv("DAY",LongDays[DSeg -> CurrentTime . Weekday]);
  1047.         setenv("DATE",DateBuff);
  1048.         setenv("TIME",TimeBuff);
  1049.     }
  1050. }
  1051.  
  1052.     /* MaxMemSize(MemType):
  1053.      *
  1054.      *    Returns the length of memory block of a special
  1055.      *    kind. Borrowed from Louis A. Mamakos' GfxMem 0.4.
  1056.      */
  1057.  
  1058. ULONG
  1059. MaxMemSize(ULONG MemType)
  1060. {
  1061.     ULONG             BlockSize = 0;
  1062.     struct MemHeader    *MemHeader;
  1063.  
  1064.     Forbid();
  1065.  
  1066.         /* Walk through the memory lists adding the
  1067.          * amount of memory bound to them.
  1068.          */
  1069.  
  1070.     for(MemHeader = (struct MemHeader *)SysBase -> MemList . lh_Head ; MemHeader -> mh_Node . ln_Succ ; MemHeader = (struct MemHeader *)MemHeader -> mh_Node . ln_Succ)
  1071.     {
  1072.         if(MemHeader -> mh_Attributes & MemType)
  1073.             BlockSize += ((ULONG)MemHeader -> mh_Upper - (ULONG)MemHeader -> mh_Lower);
  1074.     }
  1075.  
  1076.     Permit();
  1077.  
  1078.     return(BlockSize);
  1079. }
  1080.  
  1081.     /* FindTheBench():
  1082.      *
  1083.      *    Tries to locate the Workbench screen in the linked list
  1084.      *    of system screens. This is rather a rude method and should
  1085.      *    be exercised only while Intuition is locked.
  1086.      *
  1087.      *    This could be lot easier if using LockPubScreen().
  1088.      */
  1089.  
  1090. struct Screen *
  1091. FindTheBench()
  1092. {
  1093.     struct Screen    *WBench;
  1094.     ULONG         IntuiLock = LockIBase(NULL);
  1095.  
  1096.         /* Start with the first one. */
  1097.  
  1098.     WBench = IntuitionBase -> FirstScreen;
  1099.  
  1100.         /* Scan the list... */
  1101.  
  1102.     do
  1103.     {
  1104.             /* The type we want? */
  1105.  
  1106.         if((WBench -> Flags & SCREENTYPE) == WBENCHSCREEN)
  1107.         {
  1108.             UnlockIBase(IntuiLock);
  1109.             return(WBench);
  1110.         }
  1111.     }
  1112.     while(WBench = WBench -> NextScreen);
  1113.  
  1114.         /* Failed! */
  1115.  
  1116.     UnlockIBase(IntuiLock);
  1117.  
  1118.     return(NULL);
  1119. }
  1120.  
  1121.     /* PlayChime():
  1122.      *
  1123.      *    Plays the hour chime.
  1124.      */
  1125.  
  1126. LONG
  1127. PlayChime()
  1128. {
  1129.     LONG Rate = 1788,Length = 4,Cycles = 150,Volume = 64 / 2;
  1130.  
  1131.     if(ChimeReplyPort)
  1132.         return(FALSE);
  1133.  
  1134.     ObtainSemaphore(DSeg -> SoundSemaphore);
  1135.  
  1136.     if(DSeg -> SoundData && DSeg -> SoundLength)
  1137.     {
  1138.         ChimeWaveMap    = (UBYTE *)DSeg -> SoundData;
  1139.         Length        = DSeg -> SoundLength;
  1140.         Volume        = DSeg -> SoundVolume;
  1141.         Rate        = DSeg -> SoundRate;
  1142.         Cycles        = 1;
  1143.     }
  1144.     else
  1145.         ChimeWaveMap = &SquareWave[0];
  1146.  
  1147.         /* Allocate some driver memory. */
  1148.  
  1149.     if(!(ChimeAudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR)))
  1150.     {
  1151.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1152.         return(FALSE);
  1153.     }
  1154.  
  1155.         /* Time for a replyport? */
  1156.  
  1157.     if(!(ChimeReplyPort = (struct MsgPort *)CreatePort(NULL,0)))
  1158.     {
  1159.         FreeMem(ChimeAudioBlock,sizeof(struct IOAudio));
  1160.  
  1161.         ChimeAudioBlock = NULL;
  1162.  
  1163.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1164.         return(FALSE);
  1165.     }
  1166.  
  1167.         /* Set up initial driver data. */
  1168.  
  1169.     ChimeAudioBlock -> ioa_Data                    = &AnyChannel[0];
  1170.     ChimeAudioBlock -> ioa_Length                    = 4;
  1171.     ChimeAudioBlock -> ioa_Request . io_Message . mn_ReplyPort    = ChimeReplyPort;
  1172.     ChimeAudioBlock -> ioa_Request . io_Message . mn_Node . ln_Pri    = 90;
  1173.  
  1174.         /* Allocate the channels on the fly. */
  1175.  
  1176.     if(OpenDevice(AUDIONAME,0,ChimeAudioBlock,0))
  1177.     {
  1178.         FreeMem(ChimeAudioBlock,sizeof(struct IOAudio));
  1179.         DeletePort(ChimeReplyPort);
  1180.  
  1181.         ChimeAudioBlock    = NULL;
  1182.         ChimeReplyPort    = NULL;
  1183.  
  1184.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1185.         return(FALSE);
  1186.     }
  1187.  
  1188.     ChimeAudioBlock -> ioa_Request . io_Command    = CMD_WRITE;
  1189.     ChimeAudioBlock -> ioa_Request . io_Flags    = ADIOF_PERVOL;
  1190.     ChimeAudioBlock -> ioa_Cycles            = Cycles;
  1191.     ChimeAudioBlock -> ioa_Data            = ChimeWaveMap;
  1192.     ChimeAudioBlock -> ioa_Length            = Length;
  1193.  
  1194.     ChimeAudioBlock -> ioa_Period            = Rate;
  1195.     ChimeAudioBlock -> ioa_Volume            = Volume;
  1196.  
  1197.     BeginIO(ChimeAudioBlock);
  1198. }
  1199.  
  1200.     /* StopChime():
  1201.      *
  1202.      *    Stops the hour chime.
  1203.      */
  1204.  
  1205. VOID
  1206. StopChime()
  1207. {
  1208.     if(ChimeAudioBlock)
  1209.     {
  1210.         if(ChimeAudioBlock -> ioa_Request . io_Device)
  1211.         {
  1212.             if(!CheckIO(ChimeAudioBlock))
  1213.                 WaitIO(ChimeAudioBlock);
  1214.  
  1215.             CloseDevice(ChimeAudioBlock);
  1216.         }
  1217.  
  1218.         if(!DSeg -> SoundData || !DSeg -> SoundLength)
  1219.         {
  1220.             if(ChimeWaveMap != &SquareWave[0])
  1221.                 FreeMem(ChimeWaveMap,4);
  1222.         }
  1223.  
  1224.         FreeMem(ChimeAudioBlock,sizeof(struct IOAudio));
  1225.         DeletePort(ChimeReplyPort);
  1226.  
  1227.         ChimeAudioBlock    = NULL;
  1228.         ChimeReplyPort    = NULL;
  1229.  
  1230.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1231.     }
  1232. }
  1233.  
  1234.     /* FindChunk(ChunkName,FileHandle):
  1235.      *
  1236.      *    Tries to locate an iff-chunk inside a file.
  1237.      */
  1238.  
  1239. LONG
  1240. FindChunk(ULONG ChunkName,BPTR FileHandle)
  1241. {
  1242.     LONG     OldPosition;
  1243.     ULONG    FormType = 0;
  1244.  
  1245.         /* The format of a typical IFF-chunk. */
  1246.  
  1247.     struct
  1248.     {
  1249.         ULONG    IFF_Type;
  1250.         ULONG    IFF_Length;
  1251.     } Chunk;
  1252.  
  1253.         /* Remember initial file position. */
  1254.  
  1255.     OldPosition = Seek(FileHandle,0,OFFSET_CURRENT);
  1256.  
  1257.         /* Try to find it. */
  1258.  
  1259.     FOREVER
  1260.     {
  1261.             /* Read the first bytes. */
  1262.  
  1263.         if(Read(FileHandle,&Chunk,sizeof(Chunk)) != sizeof(Chunk))
  1264.         {
  1265.             Seek(FileHandle,OldPosition,OFFSET_BEGINNING);
  1266.             return(FALSE);
  1267.         }
  1268.  
  1269.             /* Is it a FORM-chunk? */
  1270.  
  1271.         if(OldPosition == 0 && FormType == 0 && Chunk . IFF_Type == 'FORM')
  1272.         {
  1273.             Read(FileHandle,&FormType,sizeof(LONG));
  1274.  
  1275.                 /* Check the form type. */
  1276.  
  1277.             if(FormType == ChunkName)
  1278.                 return(TRUE);
  1279.  
  1280.             continue;
  1281.         }
  1282.  
  1283.             /* Is it the chunk type we want? */
  1284.  
  1285.         if(Chunk . IFF_Type == ChunkName)
  1286.             return(TRUE);
  1287.  
  1288.             /* Skip chunk. */
  1289.  
  1290.         Seek(FileHandle,Chunk . IFF_Length,OFFSET_CURRENT);
  1291.     }
  1292. }
  1293.  
  1294.     /* LoadChimeSound(Name):
  1295.      *
  1296.      *    Loads an IFF-8SVX-soundfile to be used as the hour
  1297.      *    chime sound.
  1298.      */
  1299.  
  1300. LONG
  1301. LoadChimeSound(char *Name)
  1302. {
  1303.     BPTR SoundHandle;
  1304.  
  1305.     APTR SoundData;
  1306.     LONG SoundRate,SoundLength,SoundVolume;
  1307.  
  1308.         /* The format of the VoiceHeader-chunk. */
  1309.  
  1310.     struct
  1311.     {
  1312.         ULONG    oneShotHiSamples,
  1313.             repeatHiSamples,
  1314.             samplesPerHiCycle;
  1315.         UWORD    samplesPerSec;
  1316.         UBYTE    ctOctave,
  1317.             sCompression;
  1318.         LONG    volume;
  1319.     } VoiceHeader;
  1320.  
  1321.         /* No name? Reset to defaults. */
  1322.  
  1323.     if(!RexxStrCmp(Name,"OFF"))
  1324.     {
  1325.         ObtainSemaphore(DSeg -> SoundSemaphore);
  1326.  
  1327.             /* Free the memory. */
  1328.  
  1329.         if(DSeg -> SoundLength && DSeg -> SoundData)
  1330.             FreeMem(DSeg -> SoundData,DSeg -> SoundLength);
  1331.  
  1332.             /* Mark sound slot as vacant. */
  1333.  
  1334.         DSeg -> SoundLength    = 0;
  1335.         DSeg -> SoundData    = NULL;
  1336.  
  1337.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1338.  
  1339.         return(TRUE);
  1340.     }
  1341.  
  1342.         /* Open the file for reading. */
  1343.  
  1344.     if(!(SoundHandle = Open(Name,MODE_OLDFILE)))
  1345.         return(FALSE);
  1346.  
  1347.         /* Is it a sound file? */
  1348.  
  1349.     if(!FindChunk('8SVX',SoundHandle))
  1350.     {
  1351.         Close(SoundHandle);
  1352.  
  1353.         return(FALSE);
  1354.     }
  1355.  
  1356.         /* Look for the VoiceHeader. */
  1357.  
  1358.     if(!FindChunk('VHDR',SoundHandle))
  1359.     {
  1360.         Close(SoundHandle);
  1361.         return(FALSE);
  1362.     }
  1363.  
  1364.         /* Read the header. */
  1365.  
  1366.     if(Read(SoundHandle,&VoiceHeader,sizeof(VoiceHeader)) != sizeof(VoiceHeader))
  1367.     {
  1368.         Close(SoundHandle);
  1369.         return(FALSE);
  1370.     }
  1371.  
  1372.         /* Fill in the more important information. */
  1373.  
  1374.     SoundLength    = VoiceHeader . oneShotHiSamples + VoiceHeader . repeatHiSamples;
  1375.     SoundRate    = ((GfxBase -> DisplayFlags & PAL) ? 3546895 : 3579545) / VoiceHeader . samplesPerSec;
  1376.     SoundVolume    = (VoiceHeader . volume > 64 ? 64 : VoiceHeader . volume);
  1377.  
  1378.         /* Proceed with the body chunk. */
  1379.  
  1380.     if(!FindChunk('BODY',SoundHandle))
  1381.     {
  1382.         Close(SoundHandle);
  1383.         return(FALSE);
  1384.     }
  1385.  
  1386.         /* Allocate space for the sound data. */
  1387.  
  1388.     if(!(SoundData = (APTR)AllocMem(SoundLength,MEMF_PUBLIC | MEMF_CHIP)))
  1389.     {
  1390.         Close(SoundHandle);
  1391.         return(FALSE);
  1392.     }
  1393.  
  1394.         /* Read the sound data. */
  1395.  
  1396.     if(Read(SoundHandle,SoundData,SoundLength) != SoundLength)
  1397.     {
  1398.         FreeMem(SoundData,SoundLength);
  1399.         Close(SoundHandle);
  1400.         return(FALSE);
  1401.     }
  1402.  
  1403.         /* Close the file. */
  1404.  
  1405.     Close(SoundHandle);
  1406.  
  1407.         /* Lock access to sound data. */
  1408.  
  1409.     ObtainSemaphore(DSeg -> SoundSemaphore);
  1410.  
  1411.         /* Free last sound. */
  1412.  
  1413.     if(DSeg -> SoundLength && DSeg -> SoundData)
  1414.         FreeMem(DSeg -> SoundData,DSeg -> SoundLength);
  1415.  
  1416.         /* Fill in the data. */
  1417.  
  1418.     DSeg -> SoundData    = SoundData;
  1419.     DSeg -> SoundLength    = SoundLength;
  1420.     DSeg -> SoundRate    = SoundRate;
  1421.     DSeg -> SoundVolume    = SoundVolume;
  1422.  
  1423.         /* Unlock it again. */
  1424.  
  1425.     ReleaseSemaphore(DSeg -> SoundSemaphore);
  1426.  
  1427.     return(TRUE);
  1428. }
  1429.  
  1430.     /* CheckDClockStatus(Arg):
  1431.      *
  1432.      *    Checks DClock option flags and returns them.
  1433.      */
  1434.  
  1435. STRPTR
  1436. CheckDClockStatus(STRPTR Arg)
  1437. {
  1438.         /* Static result string. */
  1439.  
  1440.     STATIC UBYTE Response[200];
  1441.  
  1442.         /* Every option to be checked. */
  1443.  
  1444.     STATIC STRPTR Options[17] =
  1445.     {
  1446.         "BEEP",
  1447.         "CLICK",
  1448.         "CLICKVOLUME",
  1449.         "PRIORITY",
  1450.         "TEXTCOLOUR",
  1451.         "BACKCOLOUR",
  1452.         "ALARM",
  1453.         "ALARMTIME",
  1454.         "SETENV",
  1455.         "VERSION",
  1456.         "COUNTDOWN",
  1457.         "HOUR",
  1458.         "SECONDS",
  1459.         "SOUND",
  1460.         "PAGE",
  1461.         "SPEECH",
  1462.         "LINE"
  1463.     };
  1464.  
  1465.         /* A temporary string. */
  1466.  
  1467.     UBYTE TempString[20];
  1468.  
  1469.     LONG i,TheOption = -1;
  1470.  
  1471.         /* Clear the string. */
  1472.  
  1473.     Response[0] = 0;
  1474.  
  1475.         /* Check which option matches the argument.
  1476.          * If none matches we'll produce a string
  1477.          * filled with all options.
  1478.          */
  1479.  
  1480.     for(i = 0 ; i < 16 ; i++)
  1481.     {
  1482.         if(!RexxStrCmp(Arg,Options[i]))
  1483.         {
  1484.             TheOption = i;
  1485.             break;
  1486.         }
  1487.     }
  1488.  
  1489.         /* Is it BEEP? */
  1490.  
  1491.     if(TheOption == 0 || TheOption == -1)
  1492.     {
  1493.         if(TheOption == -1)
  1494.         {
  1495.             strcat(Response,Options[0]);
  1496.             strcat(Response," ");
  1497.         }
  1498.  
  1499.         strcat(Response,(DSeg -> Beep ? "ON" : "OFF"));
  1500.     }
  1501.  
  1502.         /* Is it CLICK? */
  1503.  
  1504.     if(TheOption == 1 || TheOption == -1)
  1505.     {
  1506.         if(TheOption == -1)
  1507.         {
  1508.             strcat(Response," ");
  1509.             strcat(Response,Options[1]);
  1510.             strcat(Response," ");
  1511.         }
  1512.  
  1513.         strcat(Response,(DSeg -> Click ? "ON" : "OFF"));
  1514.     }
  1515.  
  1516.         /* Is it CLICKVOLUME? */
  1517.  
  1518.     if(TheOption == 2 || TheOption == -1)
  1519.     {
  1520.         if(TheOption == -1)
  1521.         {
  1522.             strcat(Response," ");
  1523.             strcat(Response,Options[2]);
  1524.             strcat(Response," ");
  1525.         }
  1526.  
  1527.         BuildValueString(DSeg -> ClickVolume,TempString);
  1528.  
  1529.         strcat(Response,TempString);
  1530.     }
  1531.  
  1532.         /* Is it PRIORITY? */
  1533.  
  1534.     if(TheOption == 3 || TheOption == -1)
  1535.     {
  1536.         if(TheOption == -1)
  1537.         {
  1538.             strcat(Response," ");
  1539.             strcat(Response,Options[3]);
  1540.             strcat(Response," ");
  1541.         }
  1542.  
  1543.         BuildValueString(DSeg -> Priority,TempString);
  1544.  
  1545.         strcat(Response,TempString);
  1546.     }
  1547.  
  1548.         /* Is it TEXTCOLOUR? */
  1549.  
  1550.     if(TheOption == 4 || TheOption == -1)
  1551.     {
  1552.         if(TheOption == -1)
  1553.         {
  1554.             strcat(Response," ");
  1555.             strcat(Response,Options[4]);
  1556.             strcat(Response," ");
  1557.         }
  1558.  
  1559.         BuildValueString(DSeg -> TextColour,TempString);
  1560.  
  1561.         strcat(Response,TempString);
  1562.     }
  1563.  
  1564.         /* Is it BACKCOLOUR? */
  1565.  
  1566.     if(TheOption == 5 || TheOption == -1)
  1567.     {
  1568.         if(TheOption == -1)
  1569.         {
  1570.             strcat(Response," ");
  1571.             strcat(Response,Options[5]);
  1572.             strcat(Response," ");
  1573.         }
  1574.  
  1575.         BuildValueString(DSeg -> Priority,TempString);
  1576.  
  1577.         strcat(Response,TempString);
  1578.     }
  1579.  
  1580.         /* Is it ALARM? */
  1581.  
  1582.     if(TheOption == 6 || TheOption == -1)
  1583.     {
  1584.         if(TheOption == -1)
  1585.         {
  1586.             strcat(Response," ");
  1587.             strcat(Response,Options[6]);
  1588.             strcat(Response," ");
  1589.         }
  1590.  
  1591.         strcat(Response,(DSeg -> Alarm ? "ON" : "OFF"));
  1592.     }
  1593.  
  1594.         /* Is it ALARMTIME? */
  1595.  
  1596.     if(TheOption == 7 || TheOption == -1)
  1597.     {
  1598.         if(TheOption == -1)
  1599.         {
  1600.             strcat(Response," ");
  1601.             strcat(Response,Options[7]);
  1602.             strcat(Response," ");
  1603.         }
  1604.  
  1605.         Format(TempString,"%02ld:%02ld:%02ld",DSeg -> AlarmHour,DSeg -> AlarmMinute,DSeg -> AlarmSecond);
  1606.  
  1607.         strcat(Response,TempString);
  1608.     }
  1609.  
  1610.         /* Is it SETENV? */
  1611.  
  1612.     if(TheOption == 8 || TheOption == -1)
  1613.     {
  1614.         if(TheOption == -1)
  1615.         {
  1616.             strcat(Response," ");
  1617.             strcat(Response,Options[8]);
  1618.             strcat(Response," ");
  1619.         }
  1620.  
  1621.         strcat(Response,(DSeg -> SetEnv ? "ON" : "OFF"));
  1622.     }
  1623.  
  1624.         /* Is it VERSION? */
  1625.  
  1626.     if(TheOption == 9 || TheOption == -1)
  1627.     {
  1628.         if(TheOption == -1)
  1629.         {
  1630.             strcat(Response," ");
  1631.             strcat(Response,Options[9]);
  1632.             strcat(Response," ");
  1633.         }
  1634.  
  1635.         BuildValueString(DSeg -> Revision,TempString);
  1636.  
  1637.         strcat(Response,TempString);
  1638.     }
  1639.  
  1640.         /* Is it COUNTDOWN? */
  1641.  
  1642.     if(TheOption == 10 || TheOption == -1)
  1643.     {
  1644.         if(TheOption == -1)
  1645.         {
  1646.             strcat(Response," ");
  1647.             strcat(Response,Options[10]);
  1648.             strcat(Response," ");
  1649.         }
  1650.  
  1651.         BuildValueString(DSeg -> Countdown,TempString);
  1652.  
  1653.         strcat(Response,TempString);
  1654.     }
  1655.  
  1656.         /* Is it HOUR? */
  1657.  
  1658.     if(TheOption == 11 || TheOption == -1)
  1659.     {
  1660.         if(TheOption == -1)
  1661.         {
  1662.             strcat(Response," ");
  1663.             strcat(Response,Options[11]);
  1664.             strcat(Response," ");
  1665.         }
  1666.  
  1667.         strcat(Response,(DSeg -> Hour ? "ON" : "OFF"));
  1668.     }
  1669.  
  1670.         /* Is it SECONDS? */
  1671.  
  1672.     if(TheOption == 12 || TheOption == -1)
  1673.     {
  1674.         if(TheOption == -1)
  1675.         {
  1676.             strcat(Response," ");
  1677.             strcat(Response,Options[12]);
  1678.             strcat(Response," ");
  1679.         }
  1680.  
  1681.         strcat(Response,(DSeg -> Seconds ? "ON" : "OFF"));
  1682.     }
  1683.  
  1684.         /* Is it SOUND? */
  1685.  
  1686.     if(TheOption == 13 || TheOption == -1)
  1687.     {
  1688.         if(TheOption == -1)
  1689.         {
  1690.             strcat(Response," ");
  1691.             strcat(Response,Options[13]);
  1692.             strcat(Response," ");
  1693.         }
  1694.  
  1695.         strcat(Response,((DSeg -> SoundData && DSeg -> SoundLength) ? "ON" : "OFF"));
  1696.     }
  1697.  
  1698.         /* Is it PAGE? */
  1699.  
  1700.     if(TheOption == 14 || TheOption == -1)
  1701.     {
  1702.         if(TheOption == -1)
  1703.         {
  1704.             strcat(Response," ");
  1705.             strcat(Response,Options[14]);
  1706.             strcat(Response," ");
  1707.         }
  1708.  
  1709.         BuildValueString(DSeg -> Page,TempString);
  1710.  
  1711.         strcat(Response,TempString);
  1712.     }
  1713.  
  1714.         /* Is it SPEECH? */
  1715.  
  1716.     if(TheOption == 15 || TheOption == -1)
  1717.     {
  1718.         if(TheOption == -1)
  1719.         {
  1720.             strcat(Response," ");
  1721.             strcat(Response,Options[15]);
  1722.             strcat(Response," ");
  1723.         }
  1724.  
  1725.         strcat(Response,(DSeg -> Speech ? "ON" : "OFF"));
  1726.     }
  1727.  
  1728.         /* Is it LINE? */
  1729.  
  1730.     if(TheOption == 16 || TheOption == -1)
  1731.     {
  1732.         if(TheOption == -1)
  1733.         {
  1734.             strcat(Response," ");
  1735.             strcat(Response,Options[16]);
  1736.             strcat(Response," ");
  1737.         }
  1738.  
  1739.         strcat(Response,(DSeg -> Online ? "ON" : "OFF"));
  1740.     }
  1741.  
  1742.         /* Return the result of our 'questionnaire'. */
  1743.  
  1744.     return(Response);
  1745. }
  1746.  
  1747.     /* RexxServer():
  1748.      *
  1749.      *    Rexx server subtask (process), handles the rexx commands
  1750.      *    asynchronously.
  1751.      */
  1752.  
  1753. VOID
  1754. RexxServer()
  1755. {
  1756.     struct RexxMessage    *RexxMsg;    /* Rexx signal message. */
  1757.     ULONG             SignalSet;    /* Incoming signals. */
  1758.  
  1759.     STRPTR             StringResult;    /* Result string (error message). */
  1760.     LONG             NumResult;    /* Return code. */
  1761.  
  1762.     LONG             ArgCount;    /* String counter. */
  1763.  
  1764.     UBYTE Arg1[20],Arg2[20];        /* Both arguments. */
  1765.  
  1766.         /* Get the base register (hey Manx, where's
  1767.          * the __saveds equivalent?).
  1768.          */
  1769.  
  1770.     geta4();
  1771.  
  1772.         /* Create the communication port. */
  1773.  
  1774.     if(!(RexxTaskPort = (struct MsgPort *)CreatePort(NULL,0)))
  1775.         goto Quit;
  1776.  
  1777.         /* Synchronization. */
  1778.  
  1779.     Signal(HandlerProcess,SIG_SHAKE);
  1780.  
  1781.         /* Ad infinitum. */
  1782.  
  1783.     FOREVER
  1784.     {
  1785.             /* Wait for a signal. */
  1786.  
  1787.         SignalSet = Wait(SIG_CLOSE | (1 << RexxTaskPort -> mp_SigBit));
  1788.  
  1789.             /* A rexx message? */
  1790.  
  1791.         if(SignalSet & (1 << RexxTaskPort -> mp_SigBit))
  1792.         {
  1793.                 /* Intercept all messages. */
  1794.  
  1795.             while(RexxMsg = GetMsg(RexxTaskPort))
  1796.             {
  1797.                 ArgCount = 0;
  1798.  
  1799.                     /* Get the command string. */
  1800.  
  1801.                 StringResult = GetRexxCommand(RexxMsg);
  1802.  
  1803.                     /* Get command and argument. */
  1804.  
  1805.                 GetToken(StringResult,&ArgCount,Arg1,20);
  1806.                 GetToken(StringResult,&ArgCount,Arg2,20);
  1807.  
  1808.                     /* Zero default values. */
  1809.  
  1810.                 StringResult    = NULL;
  1811.                 NumResult    = 0;
  1812.  
  1813.                     /* Adjust Click volume? */
  1814.  
  1815.                 if(!RexxStrCmp(Arg1,"CLICKVOLUME"))
  1816.                 {
  1817.                     if(GetStringValue(Arg2) > 64 || GetStringValue(Arg2) < 0)
  1818.                         NumResult = 10;
  1819.                     else
  1820.                         DSeg -> ClickVolume = GetStringValue(Arg2);
  1821.                 }
  1822.  
  1823.                     /* Adjust task priority? */
  1824.  
  1825.                 if(!RexxStrCmp(Arg1,"PRIORITY"))
  1826.                 {
  1827.                     if(GetStringValue(Arg2) > 127 || GetStringValue(Arg2) < -128)
  1828.                         NumResult = 10;
  1829.                     else
  1830.                     {
  1831.                         DSeg -> Priority = GetStringValue(Arg2);
  1832.                         SetTaskPri(DSeg -> Child,DSeg -> Priority);
  1833.                     }
  1834.                 }
  1835.  
  1836.                     /* Turn DisplayBeep() off? */
  1837.  
  1838.                 if(!RexxStrCmp(Arg1,"BEEP"))
  1839.                 {
  1840.                     if(!RexxStrCmp(Arg2,"OFF"))
  1841.                         DSeg -> Beep = FALSE;
  1842.                     else
  1843.                     {
  1844.                         if(!RexxStrCmp(Arg2,"ON"))
  1845.                             DSeg -> Beep = TRUE;
  1846.                         else
  1847.                             NumResult = 10;
  1848.                     }
  1849.                 }
  1850.  
  1851.                     /* Turn keyboard click off? */
  1852.  
  1853.                 if(!RexxStrCmp(Arg1,"CLICK"))
  1854.                 {
  1855.                     if(!RexxStrCmp(Arg2,"OFF"))
  1856.                         DSeg -> Click = FALSE;
  1857.                     else
  1858.                     {
  1859.                         if(!RexxStrCmp(Arg2,"ON"))
  1860.                             DSeg -> Click = TRUE;
  1861.                         else
  1862.                             NumResult = 10;
  1863.                     }
  1864.                 }
  1865.  
  1866.                     /* Set text colour? */
  1867.  
  1868.                 if(!RexxStrCmp(Arg1,"TEXTCOLOUR"))
  1869.                     DSeg -> TextColour = GetStringValue(Arg2);
  1870.  
  1871.                     /* Set background colour? */
  1872.  
  1873.                 if(!RexxStrCmp(Arg1,"BACKCOLOUR"))
  1874.                     DSeg -> BackColour = GetStringValue(Arg2);
  1875.  
  1876.                     /* Turn alarm on? */
  1877.  
  1878.                 if(!RexxStrCmp(Arg1,"ALARM"))
  1879.                 {
  1880.                     if(!RexxStrCmp(Arg2,"OFF"))
  1881.                         DSeg -> Alarm = FALSE;
  1882.                     else
  1883.                     {
  1884.                         if(!RexxStrCmp(Arg2,"ON"))
  1885.                             DSeg -> Alarm = TRUE;
  1886.                         else
  1887.                             NumResult = 10;
  1888.                     }
  1889.                 }
  1890.  
  1891.                     /* Set alarm time? */
  1892.  
  1893.                 if(!RexxStrCmp(Arg1,"ALARMTIME"))
  1894.                 {
  1895.                     char TimeBuff[3];
  1896.                     LONG TheTime;
  1897.  
  1898.                     TimeBuff[2] = 0;
  1899.  
  1900.                     TimeBuff[0] = Arg2[0];
  1901.                     TimeBuff[1] = Arg2[1];
  1902.  
  1903.                     TheTime = GetStringValue(TimeBuff);
  1904.  
  1905.                     if(TheTime >= 0 && TheTime <= 23)
  1906.                         DSeg -> AlarmHour = TheTime;
  1907.                     else
  1908.                         NumResult = 10;
  1909.  
  1910.                     TimeBuff[0] = Arg2[3];
  1911.                     TimeBuff[1] = Arg2[4];
  1912.  
  1913.                     TheTime = GetStringValue(TimeBuff);
  1914.  
  1915.                     if(TheTime >= 0 && TheTime <= 59)
  1916.                         DSeg -> AlarmMinute = TheTime;
  1917.                     else
  1918.                         NumResult = 10;
  1919.  
  1920.                     TimeBuff[0] = Arg2[6];
  1921.                     TimeBuff[1] = Arg2[7];
  1922.  
  1923.                     TheTime = GetStringValue(TimeBuff);
  1924.  
  1925.                     if(TheTime >= 0 && TheTime <= 59)
  1926.                         DSeg -> AlarmSecond = TheTime;
  1927.                     else
  1928.                         NumResult = 10;
  1929.                 }
  1930.  
  1931.                     /* Set environment variables? */
  1932.  
  1933.                 if(!RexxStrCmp(Arg1,"SETENV"))
  1934.                 {
  1935.                     if(!RexxStrCmp(Arg2,"OFF"))
  1936.                         DSeg -> SetEnv = FALSE;
  1937.                     else
  1938.                     {
  1939.                         if(!RexxStrCmp(Arg2,"ON"))
  1940.                             DSeg -> SetEnv = TRUE;
  1941.                         else
  1942.                             NumResult = 10;
  1943.                     }
  1944.                 }
  1945.  
  1946.                     /* Set the countdown tea timer? */
  1947.  
  1948.                 if(!RexxStrCmp(Arg1,"COUNTDOWN"))
  1949.                 {
  1950.                     if(GetStringValue(Arg2) > 0)
  1951.                         DSeg -> Countdown = GetStringValue(Arg2);
  1952.                     else
  1953.                         NumResult = 10;
  1954.                 }
  1955.  
  1956.                     /* Refresh the display? */
  1957.  
  1958.                 if(!RexxStrCmp(Arg1,"REFRESH"))
  1959.                 {
  1960.                     if(CloseWorkBench())
  1961.                         OpenWorkBench();
  1962.                 }
  1963.  
  1964.                     /* Turn hour alarm on? */
  1965.  
  1966.                 if(!RexxStrCmp(Arg1,"HOUR"))
  1967.                 {
  1968.                     if(!RexxStrCmp(Arg2,"OFF"))
  1969.                         DSeg -> Hour = FALSE;
  1970.                     else
  1971.                     {
  1972.                         if(!RexxStrCmp(Arg2,"ON"))
  1973.                             DSeg -> Hour = TRUE;
  1974.                         else
  1975.                             NumResult = 10;
  1976.                     }
  1977.                 }
  1978.  
  1979.                     /* Turn keyboard click off? */
  1980.  
  1981.                 if(!RexxStrCmp(Arg1,"SECONDS"))
  1982.                 {
  1983.                     if(!RexxStrCmp(Arg2,"OFF"))
  1984.                         DSeg -> Seconds = FALSE;
  1985.                     else
  1986.                     {
  1987.                         if(!RexxStrCmp(Arg2,"ON"))
  1988.                             DSeg -> Seconds = TRUE;
  1989.                         else
  1990.                             NumResult = 10;
  1991.                     }
  1992.                 }
  1993.  
  1994.                     /* Load a sound file? */
  1995.  
  1996.                 if(!RexxStrCmp(Arg1,"SOUND"))
  1997.                 {
  1998.                     if(!LoadChimeSound(Arg2))
  1999.                         NumResult = 10;
  2000.                 }
  2001.  
  2002.                     /* Get a DClock status. */
  2003.  
  2004.                 if(!RexxStrCmp(Arg1,"STATUS"))
  2005.                     StringResult = CheckDClockStatus(Arg2);
  2006.  
  2007.                     /* Show a special page? */
  2008.  
  2009.                 if(!RexxStrCmp(Arg1,"PAGE"))
  2010.                 {
  2011.                     if(GetStringValue(Arg2) > 4 || GetStringValue(Arg2) < 0 || (GetStringValue(Arg2) == 4 && DSeg -> Countdown < 1))
  2012.                         NumResult = 10;
  2013.                     else
  2014.                         DSeg -> Page = GetStringValue(Arg2);
  2015.                 }
  2016.  
  2017.                     /* Tell the current time? */
  2018.  
  2019.                 if(!RexxStrCmp(Arg1,"TELLTIME"))
  2020.                 {
  2021.                     if(!SpeechProcess)
  2022.                     {
  2023.                         Forbid();
  2024.  
  2025.                         if(SpeechProcess = CreateFuncProc("DClock-Speech",5,SpeechServer,4000))
  2026.                             Wait(SIG_SHAKE);
  2027.  
  2028.                         Permit();
  2029.                     }
  2030.  
  2031.                     if(SpeechProcess)
  2032.                         Signal(SpeechProcess,SIG_CLICK);
  2033.                 }
  2034.  
  2035.                     /* Read the clock chip? */
  2036.  
  2037.                 if(!RexxStrCmp(Arg1,"READTIME"))
  2038.                 {
  2039.                     if(!ReadClock())
  2040.                         NumResult = 10;
  2041.                 }
  2042.  
  2043.                     /* Reply the rexx command. */
  2044.  
  2045.                 ReplyRexxCommand(RexxMsg,NumResult,0,StringResult);
  2046.             }
  2047.         }
  2048.  
  2049.             /* Remove the server task. */
  2050.  
  2051.         if(SignalSet & SIG_CLOSE)
  2052.             break;
  2053.     }
  2054.  
  2055.         /* Delete the port. */
  2056.  
  2057. Quit:    if(RexxTaskPort)
  2058.         DeletePort(RexxTaskPort);
  2059.  
  2060.         /* Hey, I'm done! */
  2061.  
  2062.     Signal(HandlerProcess,SIG_SHAKE);
  2063. }
  2064.  
  2065.     /* SpeechServer():
  2066.      *
  2067.      *    Asks the narrator device to tell the time.
  2068.      */
  2069.  
  2070. VOID
  2071. SpeechServer()
  2072. {
  2073.     ULONG             SignalSet;        /* Signal mask. */
  2074.     struct MsgPort        *NarratorPort;        /* Narrator reply port. */
  2075.     struct narrator_rb    *NarratorRequest;    /* Narrator device link. */
  2076.  
  2077.     char             WorkString[40];    /* Phoneme string. */
  2078.  
  2079.         /* Numbers above twenty. */
  2080.  
  2081.     static char *AboveTwenty[4] =
  2082.     {
  2083.         "TWEH4NTIY4",
  2084.         "THER4TIY4",
  2085.         "FOH4RTIY4",
  2086.         "FIH4FTIY4"
  2087.     };
  2088.  
  2089.         /* Numbers from zero to nineteen. */
  2090.  
  2091.     static char *SingleNumbers[20] =
  2092.     {
  2093.         "ZIY4ROW",
  2094.         "WAH4N",
  2095.         "TUW4",
  2096.         "THRIY4",
  2097.         "FOH4R",
  2098.         "FAY4V",
  2099.         "SIH4KKSZ",
  2100.         "SEH4VVEHN",
  2101.         "EY4T",
  2102.         "NAY4N",
  2103.         "TEH4N",
  2104.         "IY4LAEEHFAEEHN",
  2105.         "TWEH4LF",
  2106.         "THER4TIY4N",
  2107.         "FOH4RTIY4N",
  2108.         "FIH4FTIY4N",
  2109.         "SIH4KKSZTIY4N",
  2110.         "SEH4VVEHNTIY4N",
  2111.         "EY3TIY4N",
  2112.         "NAY4NTIY4N"
  2113.     };
  2114.  
  2115.         /* The two halves of the day. */
  2116.  
  2117.     static char *TimeOfDay[2] =
  2118.     {
  2119.         " EY5EH3M.",
  2120.         " BPIY5EH3M."
  2121.     };
  2122.  
  2123.         /* Get the data base register. */
  2124.  
  2125.     geta4();
  2126.  
  2127.         /* Allocate speech driver data. */
  2128.  
  2129.     if(NarratorPort = CreatePort(NULL,0))
  2130.     {
  2131.         if(NarratorRequest = (struct narrator_rb *)CreateExtIO(NarratorPort,sizeof(struct narrator_rb)))
  2132.         {
  2133.                 /* Say which channels we need. */
  2134.  
  2135.             NarratorRequest -> ch_masks        = &AnyChannel[0];
  2136.             NarratorRequest -> nm_masks        = 4;
  2137.  
  2138.                 /* This is a write request. */
  2139.  
  2140.             NarratorRequest -> message . io_Command    = CMD_WRITE;
  2141.             NarratorRequest -> message . io_Data    = (APTR)WorkString;
  2142.  
  2143.                 /* Try to open the speech driver. */
  2144.  
  2145.             if(!OpenDevice("narrator.device",0,NarratorRequest,0))
  2146.             {
  2147.                     /* Handshake with handler process. */
  2148.  
  2149.                 Signal(HandlerProcess,SIG_SHAKE);
  2150.  
  2151.                 FOREVER
  2152.                 {
  2153.                         /* Wait for signal. */
  2154.  
  2155.                     SignalSet = Wait(SIG_CLOSE | SIG_CLICK);
  2156.  
  2157.                         /* Are we to shut down? */
  2158.  
  2159.                     if(SignalSet & SIG_CLOSE)
  2160.                         break;
  2161.  
  2162.                         /* Prevent time data from being changed. */
  2163.  
  2164.                     Forbid();
  2165.  
  2166.                     strcpy(WorkString,"IH4THZ ");
  2167.  
  2168.                         /* Is the hour below twelve? */
  2169.  
  2170.                     if(DSeg -> CurrentTime . Hour <= 12)
  2171.                         strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Hour]);
  2172.  
  2173.                         /* Is it above twelve? */
  2174.  
  2175.                     if(DSeg -> CurrentTime . Hour > 12)
  2176.                         strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Hour - 12]);
  2177.  
  2178.                     strcat(WorkString," EH4KLAA4K ");
  2179.  
  2180.                         /* Are the minutes above zero and below twenty? */
  2181.  
  2182.                     if(DSeg -> CurrentTime . Minute < 20 && DSeg -> CurrentTime . Minute > 0)
  2183.                         strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Minute]);
  2184.  
  2185.                         /* Are the minutes above twenty? */
  2186.  
  2187.                     if(DSeg -> CurrentTime . Minute >= 20)
  2188.                     {
  2189.                             /* Append the tenths first. */
  2190.  
  2191.                         strcat(WorkString,AboveTwenty[DSeg -> CurrentTime . Minute / 10 - 2]);
  2192.  
  2193.                             /* Add the minutes if any. */
  2194.  
  2195.                         if(DSeg -> CurrentTime . Minute % 10)
  2196.                             strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Minute % 10]);
  2197.                     }
  2198.  
  2199.                         /* Add a.m./p.m. */
  2200.  
  2201.                     if(DSeg -> CurrentTime . Hour > 12)
  2202.                         strcat(WorkString,TimeOfDay[1]);
  2203.                     else
  2204.                     {
  2205.                         if(DSeg -> CurrentTime . Hour > 0 && DSeg -> CurrentTime . Hour < 12)
  2206.                             strcat(WorkString,TimeOfDay[0]);
  2207.                     }
  2208.  
  2209.                     Permit();
  2210.  
  2211.                         /* Remember the length (-1 doesn't work here for some reason). */
  2212.  
  2213.                     NarratorRequest -> message . io_Length = strlen(WorkString);
  2214.  
  2215.                         /* Say it and wait for it to terminate. */
  2216.  
  2217.                     SendIO(NarratorRequest);
  2218.                     WaitIO(NarratorRequest);
  2219.  
  2220.                     GetMsg(NarratorPort);
  2221.                 }
  2222.  
  2223.                     /* Deallocate our resources and exit. */
  2224.                 
  2225.                 while(!CheckIO(NarratorRequest))
  2226.                 {
  2227.                     AbortIO(NarratorRequest);
  2228.                     WaitIO(NarratorRequest);
  2229.  
  2230.                     GetMsg(NarratorPort);
  2231.                 }
  2232.  
  2233.                 CloseDevice(NarratorRequest);
  2234.             }
  2235.  
  2236.             DeleteExtIO(NarratorRequest);
  2237.         }
  2238.  
  2239.         DeletePort(NarratorPort);
  2240.     }
  2241.  
  2242.         /* Lock & quit. */
  2243.  
  2244.     Forbid();
  2245.  
  2246.     SpeechProcess = NULL;
  2247.  
  2248.     Signal(HandlerProcess,SIG_SHAKE);
  2249. }
  2250.  
  2251.     /* CreateFuncProc():
  2252.      *
  2253.      *    Similar to CreateTask this routine will start a
  2254.      *    'C' function as a process. The technique used
  2255.      *    by this nifty little piece of code was originally
  2256.      *    conceived by Leo Schwab.
  2257.      */
  2258.  
  2259. struct Process *
  2260. CreateFuncProc(char *Name,LONG Priority,APTR InitCode,ULONG StackSize)
  2261. {
  2262.     struct Process *ChildProc = NULL;
  2263.  
  2264.     struct
  2265.     {
  2266.         ULONG    SegLength;    /* Length of segment. */
  2267.         BPTR    NextSeg;    /* Pointer to next segment. */
  2268.  
  2269.         WORD    FirstCode;    /* First instruction (JMP). */
  2270.         APTR    RealCode;    /* Address of function. */
  2271.     } *FakeSeg;
  2272.  
  2273.         /* Allocate the segment. */
  2274.  
  2275.     if(FakeSeg = (struct FakeSeg *)AllocMem(sizeof(*FakeSeg),MEMF_PUBLIC | MEMF_CLEAR))
  2276.     {
  2277.         struct MsgPort *ChildPort;
  2278.  
  2279.             /* Fill in the data. */
  2280.  
  2281.         FakeSeg -> SegLength    = sizeof(*FakeSeg);
  2282.         FakeSeg -> FirstCode    = 0x4EF9;        /* JMP EA */
  2283.         FakeSeg -> RealCode    = InitCode;        /* EA */
  2284.  
  2285.             /* Create the process. */
  2286.  
  2287.         if(ChildPort = (struct MsgPort *)CreateProc(Name,Priority,MKBADDR(&FakeSeg -> NextSeg),StackSize))
  2288.             ChildProc = (struct Process *)ChildPort -> mp_SigTask;
  2289.  
  2290.         Delay(TICKS_PER_SECOND / 2);
  2291.  
  2292.             /* Free the segment. */
  2293.  
  2294.         FreeMem(FakeSeg,sizeof(*FakeSeg));
  2295.     }
  2296.  
  2297.         /* Return the pointer to the process. */
  2298.  
  2299.     return(ChildProc);
  2300. }
  2301.  
  2302.     /* CreateDummyRPort():
  2303.      *
  2304.      *    Creates a hidden RastPort for time/graphics
  2305.      *    rendering.
  2306.      */
  2307.  
  2308. BYTE
  2309. CreateDummyRPort()
  2310. {
  2311.     struct TextAttr    FontRequest;
  2312.     SHORT        i;
  2313.  
  2314.         /* Allocate the BitMap. */
  2315.  
  2316.     if(!(DummyMap = (struct DummyMap *)AllocMem(sizeof(struct BitMap),MEMF_PUBLIC | MEMF_CLEAR)))
  2317.         return(FALSE);
  2318.  
  2319.         /* How many bitplanes are there in the display? */
  2320.  
  2321.     DummyDepth = Window -> RPort -> BitMap -> Depth;
  2322.  
  2323.         /* Initialize the bitmap pointers. */
  2324.  
  2325.     InitBitMap(DummyMap,DummyDepth,Width,Height);
  2326.  
  2327.         /* Allocate the memory. */
  2328.  
  2329.     for(i = 0 ; i < DummyDepth ; i++)
  2330.     {
  2331.         if(!(DummyMap -> Planes[i] = AllocMem(Byte(Width) * Height,MEMF_CHIP)))
  2332.             return(FALSE);
  2333.     }
  2334.  
  2335.         /* Mangle the RastPort. */
  2336.  
  2337.     if(!(DummyRPort = (struct RastPort *)AllocMem(sizeof(struct RastPort),MEMF_PUBLIC | MEMF_CLEAR)))
  2338.         return(FALSE);
  2339.  
  2340.         /* Initialize the RastPort. */
  2341.  
  2342.     InitRastPort(DummyRPort);
  2343.  
  2344.         /* Link it to the BitMap. */
  2345.  
  2346.     DummyRPort -> BitMap = DummyMap;
  2347.  
  2348.         /* Clear the drawing area. */
  2349.  
  2350.     SetRast(DummyRPort,1);
  2351.  
  2352.         /* Copy the font attributes. */
  2353.  
  2354.     FontRequest . ta_Name    = (STRPTR)Window -> IFont -> tf_Message . mn_Node . ln_Name;
  2355.     FontRequest . ta_YSize    = Window -> IFont -> tf_YSize;
  2356.     FontRequest . ta_Style    = Window -> IFont -> tf_Style;
  2357.     FontRequest . ta_Flags    = Window -> IFont -> tf_Flags;
  2358.  
  2359.         /* Can we open the font (is it already in the system
  2360.          * list)?
  2361.          */
  2362.  
  2363.     if(!(DummyFont = (struct TextFont *)OpenFont(&FontRequest)))
  2364.     {
  2365.             /* It is probably a diskfont. */
  2366.  
  2367.         if(DiskfontBase = (struct Library *)OpenLibrary("diskfont.library",0))
  2368.         {
  2369.             if(!(DummyFont = (struct TextFont *)OpenDiskFont(&FontRequest)))
  2370.                 return(FALSE);
  2371.             else
  2372.                 CloseLibrary(DiskfontBase);
  2373.         }
  2374.     }
  2375.  
  2376.         /* Attach the font to the RastPort. */
  2377.  
  2378.     SetFont(DummyRPort,DummyFont);
  2379.  
  2380.         /* And set the drawing mode. */
  2381.  
  2382.     SetDrMd(DummyRPort,JAM2);
  2383.  
  2384.     return(TRUE);
  2385. }
  2386.  
  2387.     /* DeleteDummyRPort():
  2388.      *
  2389.      *    Removes the hidden RastPort from memory and get rid
  2390.      *    of the font attached to it.
  2391.      */
  2392.  
  2393. VOID
  2394. DeleteDummyRPort()
  2395. {
  2396.     SHORT i;
  2397.  
  2398.     if(DummyMap)
  2399.     {
  2400.         for(i = 0 ; i < DummyDepth ; i++)
  2401.             if(DummyMap -> Planes[i])
  2402.                 FreeMem(DummyMap -> Planes[i],Byte(Width) * Height);
  2403.  
  2404.         FreeMem(DummyMap,sizeof(struct BitMap));
  2405.  
  2406.         DummyMap = NULL;
  2407.     }
  2408.  
  2409.     if(DummyRPort)
  2410.     {
  2411.         FreeMem(DummyRPort,sizeof(struct RastPort));
  2412.         DummyRPort = NULL;
  2413.     }
  2414.  
  2415.     if(DummyFont)
  2416.     {
  2417.         CloseFont(DummyFont);
  2418.         DummyFont = NULL;
  2419.     }
  2420. }
  2421.  
  2422.     /* MaxFontWidth():
  2423.      *
  2424.      *    Calculate the maximum width of the display by looking for
  2425.      *    the widest letter.
  2426.      */
  2427.  
  2428. UBYTE
  2429. MaxFontWidth()
  2430. {
  2431.     BYTE    FontWidth = 0,TempWidth;
  2432.     char    Shuttle[2];
  2433.     SHORT    i;
  2434.  
  2435.         /* We cannot use a single character to check the width
  2436.          * of a letter, so we'll fake a string.
  2437.          */
  2438.  
  2439.     Shuttle[1] = 0;
  2440.  
  2441.         /* Check all letters. */
  2442.  
  2443.     for(i = 0 ; i < 256 ; i++)
  2444.     {
  2445.         Shuttle[0] = i;
  2446.  
  2447.             /* Is it wider than the last letter. */
  2448.  
  2449.         if((TempWidth = TextLength(Window -> RPort,Shuttle,1)) > FontWidth)
  2450.             FontWidth = TempWidth;
  2451.     }
  2452.  
  2453.     return(FontWidth);
  2454. }
  2455.  
  2456.     /* ShutDown(HandShake):
  2457.      *
  2458.      *    Closes DClock and waits for removal.
  2459.      */
  2460.  
  2461. VOID
  2462. ShutDown(LONG HandShake)
  2463. {
  2464.         /* Restore system functions. */
  2465.  
  2466.     if(OldDisplayBeep)
  2467.         SetFunction((struct Library *)IntuitionBase,-0x60,OldDisplayBeep);
  2468.  
  2469.     if(OldCloseWBench)
  2470.         SetFunction((struct Library *)IntuitionBase,-0x4E,OldCloseWBench);
  2471.  
  2472.     FlushHandler();
  2473.     FlushSound();
  2474.  
  2475.     if(Window)
  2476.     {
  2477.         SetRast(DummyRPort,1);
  2478.  
  2479.         ClipBlit(DummyRPort,0,0,Window -> WScreen -> BarLayer -> rp,LeftEdge,1,Width,Height,0xC0);
  2480.  
  2481.         CloseWindow(Window);
  2482.     }
  2483.  
  2484.     DeleteDummyRPort();
  2485.  
  2486.         /* Return the special signals. */
  2487.  
  2488.     if(BenchSig != -1)
  2489.         FreeSignal(BenchSig);
  2490.  
  2491.     if(DisplaySig != 1)
  2492.         FreeSignal(DisplaySig);
  2493.  
  2494.     if(SpeechSig != 1)
  2495.         FreeSignal(SpeechSig);
  2496.  
  2497.         /* Free the Rexx host port. */
  2498.  
  2499.     if(DSeg -> RexxHost)
  2500.         DeleteRexxHost(DSeg -> RexxHost);
  2501.  
  2502.     if(RexxProcess)
  2503.     {
  2504.         Signal(RexxProcess,SIG_CLOSE);
  2505.  
  2506.         Wait(SIG_SHAKE);
  2507.     }
  2508.  
  2509.     if(SpeechProcess)
  2510.     {
  2511.         Signal(SpeechProcess,SIG_CLOSE);
  2512.  
  2513.         Wait(SIG_SHAKE);
  2514.     }
  2515.  
  2516.     if(RexxHostBase)
  2517.         CloseLibrary(RexxHostBase);
  2518.  
  2519.     if(IntuitionBase)
  2520.         CloseLibrary(IntuitionBase);
  2521.  
  2522.     if(GfxBase)
  2523.         CloseLibrary(GfxBase);
  2524.  
  2525.         /* Sneak out before someone can
  2526.          * UnLoadSeg() us.
  2527.          */
  2528.  
  2529.     Forbid();
  2530.  
  2531.         /* Goodbye father. */
  2532.  
  2533.     if(DSeg -> Father)
  2534.         Signal(DSeg -> Father,HandShake);
  2535. }
  2536.  
  2537.     /* _main():
  2538.      *
  2539.      *    Modified Aztec C startup-routine, no CLI parsing,
  2540.      *    no Workbench parsing, absolutely nothing.
  2541.      */
  2542.  
  2543. LONG
  2544. _main()
  2545. {
  2546.     volatile ULONG         MemSize,MaxSize;
  2547.  
  2548.     ULONG             SignalSet;
  2549.     UBYTE             Force,Blink = FALSE,i,EmptyWidth,FullWidth;
  2550.  
  2551.     LONG             RexxMask = 0;
  2552.  
  2553.     struct Screen        *Workbench;
  2554.  
  2555.     HandlerProcess = (struct Process *)SysBase -> ThisTask;
  2556.  
  2557.         /* If somebody called us from CLI... */
  2558.  
  2559.     if(HandlerProcess -> pr_CLI)
  2560.         return(-1);
  2561.  
  2562.         /* Is the DSeg structure anywhere? */
  2563.  
  2564.     if(!(DSeg = (struct DSeg *)FindPort(PORTNAME)))
  2565.     {
  2566.         Forbid();
  2567.         Signal(DSeg -> Father,DSeg -> RingBack);
  2568.  
  2569.         return;
  2570.     }
  2571.  
  2572.         /* Check if it's below our current revision
  2573.          * number (probably doesn't support some
  2574.          * structure tags).
  2575.          */
  2576.  
  2577.     if(DSeg -> Revision < REVISION)
  2578.     {
  2579.         Forbid();
  2580.         Signal(DSeg -> Father,DSeg -> RingBack);
  2581.  
  2582.         return;
  2583.     }
  2584.  
  2585.         /* Open those libraries. */
  2586.  
  2587.     if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0)))
  2588.     {
  2589.         Forbid();
  2590.         Signal(DSeg -> Father,DSeg -> RingBack);
  2591.  
  2592.         return;
  2593.     }
  2594.  
  2595.     if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0)))
  2596.     {
  2597.         ShutDown(DSeg -> RingBack);
  2598.  
  2599.         return;
  2600.     }
  2601.  
  2602.         /* Open RexxHostLibrary if possible. If the open
  2603.          * fails, it doesn't matter.
  2604.          */
  2605.  
  2606.     RexxHostBase = OpenLibrary("rexxhost.library",34);
  2607.  
  2608.         /* Try to find the Workbench screen. */
  2609.  
  2610.     if(!(Workbench = (struct Screen *)FindTheBench()))
  2611.     {
  2612.         ShutDown(DSeg -> RingBack);
  2613.  
  2614.         return;
  2615.     }
  2616.  
  2617.         /* Open the backdrop window. */
  2618.  
  2619.     if(!(Window = (struct Window *)OpenWindow(&NewWindow)))
  2620.     {
  2621.         ShutDown(DSeg -> RingBack);
  2622.  
  2623.         return;
  2624.     }
  2625.  
  2626.         /* Initialize the click sound data. */
  2627.  
  2628.     if(!InitSound())
  2629.     {
  2630.         ShutDown(DSeg -> RingBack);
  2631.  
  2632.         return;
  2633.     }
  2634.  
  2635.         /* Install the handler code. */
  2636.  
  2637.     if(!InitHandler())
  2638.     {
  2639.         ShutDown(DSeg -> RingBack);
  2640.  
  2641.         return;
  2642.     }
  2643.  
  2644.     if((BenchSig = AllocSignal(-1)) == -1)
  2645.     {
  2646.         ShutDown(DSeg -> RingBack);
  2647.  
  2648.         return;
  2649.     }
  2650.  
  2651.     if((DisplaySig = AllocSignal(-1)) == -1)
  2652.     {
  2653.         ShutDown(DSeg -> RingBack);
  2654.  
  2655.         return;
  2656.     }
  2657.  
  2658.     if((SpeechSig = AllocSignal(-1)) == -1)
  2659.     {
  2660.         ShutDown(DSeg -> RingBack);
  2661.  
  2662.         return;
  2663.     }
  2664.  
  2665.         /* Adjust the window left offset. */
  2666.  
  2667.     Width        = MaxFontWidth() * 22;
  2668.     Height        = Window -> IFont -> tf_YSize;
  2669.  
  2670.     LeftEdge    = Workbench -> Width - 55 - Width;
  2671.  
  2672.     EmptyWidth    = TextLength(Window -> RPort,"E",1);
  2673.     FullWidth    = TextLength(Window -> RPort,"F",1);
  2674.  
  2675.         /* Create the dummy RastPort. */
  2676.  
  2677.     if(!CreateDummyRPort())
  2678.     {
  2679.         ShutDown(DSeg -> RingBack);
  2680.  
  2681.         return;
  2682.     }
  2683.  
  2684.         /* Check if we are running unter Kickstart 2.x. */
  2685.  
  2686.     if(SysBase -> LibNode . lib_Version >= 36)
  2687.     {
  2688.         USHORT TempCol;
  2689.  
  2690.         NewKick = TRUE;
  2691.  
  2692.         for(i = 0 ; i < 228 ; i++)
  2693.         {
  2694.             TempCol = ClockMap[i];
  2695.             ClockMap[i] = ClockMap[i + 228];
  2696.             ClockMap[i + 228] = TempCol;
  2697.         }
  2698.  
  2699.         for(i = 0 ; i < 3 ; i++)
  2700.             ClockTxt[i] . FrontPen = 1;
  2701.  
  2702.         LeftEdge = Workbench -> Width - 25 - Width;
  2703.     }
  2704.  
  2705.         /* Create the rexx server task. */
  2706.  
  2707.     if(RexxHostBase)
  2708.     {
  2709.         if(!(RexxProcess = (struct Process *)CreateFuncProc("DClock-Rexx",5,RexxServer,4000)))
  2710.         {
  2711. Shut:            ShutDown(DSeg -> RingBack);
  2712.  
  2713.             return;
  2714.         }
  2715.  
  2716.             /* Wait for handshake. */
  2717.  
  2718.         Wait(SIG_SHAKE);
  2719.  
  2720.             /* No port? Fail fast! */
  2721.  
  2722.         if(!RexxTaskPort)
  2723.             goto Shut;
  2724.     }
  2725.  
  2726.         /* Fill in the window and bring it to the front. */
  2727.  
  2728.     ShowTime(FALSE,TRUE);
  2729.  
  2730.         /* Patch system function. */
  2731.  
  2732.     OldDisplayBeep = SetFunction((struct Library *)IntuitionBase,-0x60,NewDisplayBeep);
  2733.     OldCloseWBench = SetFunction((struct Library *)IntuitionBase,-0x4E,NewCloseWBench);
  2734.  
  2735.         /* If RexxHostBase is around, add the rexx port. */
  2736.  
  2737.     if(RexxHostBase)
  2738.     {
  2739.         if(DSeg -> RexxHost = CreateRexxHost("DCLOCK"))
  2740.             RexxMask = (1 << DSeg -> RexxHost -> rh_Port . mp_SigBit);
  2741.     }
  2742.  
  2743.         /* Initialize the signal-semaphore. */
  2744.  
  2745.     InitSemaphore(DSeg -> SoundSemaphore);
  2746.  
  2747.         /* Now we are truly running. */
  2748.  
  2749.     DSeg -> Child = SysBase -> ThisTask;
  2750.  
  2751.         /* How much memory is there around in this
  2752.          * Amiga?
  2753.          */
  2754.  
  2755.     MaxSize = MaxMemSize(MEMF_CHIP) + MaxMemSize(MEMF_FAST);
  2756.  
  2757.         /* Tell father to finish. */
  2758.  
  2759.     Signal(DSeg -> Father,DSeg -> RingBack);
  2760.  
  2761.     DSeg -> Father = NULL;
  2762.  
  2763.         /* Go into infinite loop waiting for signals. */
  2764.  
  2765.     FOREVER
  2766.     {
  2767.         SignalSet = Wait(SIG_TIMER | SIG_CLOSE | SIG_CLICK | SIG_TOGGL | SIG_BENCH | SIG_WINDO | SIG_DISPL | SIG_SPEECH | RexxMask);
  2768.  
  2769.             /* Was it a Rexx call? */
  2770.  
  2771.         if((SignalSet & RexxMask) && RexxMask)
  2772.         {
  2773.             struct RexxMessage *RexxMsg;
  2774.  
  2775.                 /* Capture the messages... */
  2776.  
  2777.             while(RexxMsg = GetMsg((struct MsgPort *)DSeg -> RexxHost))
  2778.             {
  2779.                     /* Send them to the rexx server task. */
  2780.  
  2781.                 if(GetRexxCommand(RexxMsg))
  2782.                     PutMsg(RexxTaskPort,RexxMsg);
  2783.                 else
  2784.                     FreeRexxCommand(RexxMsg);
  2785.             }
  2786.         }
  2787.  
  2788.             /* Change the display mode? */
  2789.  
  2790.         if(SignalSet & SIG_TOGGL)
  2791.         {
  2792.             DSeg -> Page++;
  2793.  
  2794.             SetRast(DummyRPort,1);
  2795.  
  2796.             if(DSeg -> Page == 5)
  2797.                 DSeg -> Page = 0;
  2798.  
  2799.             if(DSeg -> Countdown < 1 && DSeg -> Page == 4)
  2800.                 DSeg -> Page = 0;
  2801.  
  2802.             Force = TRUE;
  2803.         }
  2804.  
  2805.             /* Are we to click? */
  2806.  
  2807.         if((SignalSet & SIG_CLICK) && DSeg -> Click)
  2808.             Click();
  2809.  
  2810.             /* Are we to shut down? */
  2811.  
  2812.         if(SignalSet & SIG_CLOSE)
  2813.         {
  2814.             if(ChimeAudioBlock)
  2815.                 StopChime();
  2816.  
  2817.             ShutDown(SIG_CLOSE);
  2818.             return;
  2819.         }
  2820.  
  2821.         if(SignalSet & SIG_BENCH)
  2822.         {
  2823.                 /* Close the window... */
  2824.  
  2825.             if(Window)
  2826.             {
  2827.                 CloseWindow(Window);
  2828.                 Window = NULL;
  2829.  
  2830.                 DeleteDummyRPort();
  2831.  
  2832.                 Delay(25);
  2833.  
  2834.                 FOREVER
  2835.                 {
  2836.                         /* Wait for wakeup call... */
  2837.  
  2838.                     if((SetSignal(NULL,NULL) & SIG_CLOSE) == SIG_CLOSE)
  2839.                     {
  2840.                         SetSignal(NULL,SIG_CLOSE);
  2841.  
  2842.                         SignalSet = SIG_CLOSE;
  2843.                         break;
  2844.                     }
  2845.  
  2846.                     if(Workbench = (struct Screen *)FindTheBench())
  2847.                     {
  2848.                         SignalSet = NULL;
  2849.                         break;
  2850.                     }
  2851.  
  2852.                     Delay(25);
  2853.                 }
  2854.             }
  2855.  
  2856.                 /* Finish? */
  2857.  
  2858.             if(SignalSet & SIG_CLOSE)
  2859.             {
  2860.                 if(ChimeAudioBlock)
  2861.                     StopChime();
  2862.  
  2863.                 ShutDown(SIG_CLOSE);
  2864.                 return;
  2865.             }
  2866.  
  2867.                 /* Open the window. */
  2868.  
  2869.             if(!(Window = (struct Window *)OpenWindow(&NewWindow)))
  2870.             {
  2871. BeatIt:                if(ChimeAudioBlock)
  2872.                     StopChime();
  2873.  
  2874.                 ShutDown(SIG_CLOSE);
  2875.                 return;
  2876.             }
  2877.  
  2878.             Width    = MaxFontWidth() * 22;
  2879.             Height    = Window -> IFont -> tf_YSize;
  2880.  
  2881.                 /* Re-adjust the Window offset. */
  2882.  
  2883.             if(NewKick)
  2884.                 LeftEdge = Workbench -> Width - 25 - Width;
  2885.             else
  2886.                 LeftEdge = Workbench -> Width - 55 - Width;
  2887.  
  2888.             EmptyWidth    = TextLength(Window -> RPort,"E",1);
  2889.             FullWidth    = TextLength(Window -> RPort,"F",1);
  2890.  
  2891.                 /* Redraw the time if necessary. */
  2892.  
  2893.             Printed = FALSE;
  2894.  
  2895.             if(!CreateDummyRPort())
  2896.                 goto BeatIt;
  2897.         }
  2898.  
  2899.             /* A window refresh call came in. */
  2900.  
  2901.         if(SignalSet & SIG_WINDO)
  2902.         {
  2903.             struct IntuiMessage *IMsg;
  2904.  
  2905.                 /* Reply the Message (don't need it). */
  2906.  
  2907.             if(IMsg = GetMsg(Window -> UserPort))
  2908.                 ReplyMsg(IMsg);
  2909.         }
  2910.  
  2911.             /* Give it a brief beep to indicate the hour? */
  2912.  
  2913.         if(DSeg -> Hour && !DSeg -> CurrentTime . Minute && !DSeg -> CurrentTime . Second)
  2914.             PlayChime();
  2915.  
  2916.             /* Check if chime has ended. */
  2917.  
  2918.         if(ChimeAudioBlock)
  2919.         {
  2920.             if(CheckIO(ChimeAudioBlock))
  2921.                 StopChime();
  2922.         }
  2923.  
  2924.             /* Ready to ring the bell? */
  2925.  
  2926.         if(DSeg -> CurrentTime . Hour   == DSeg -> AlarmHour &&
  2927.            DSeg -> CurrentTime . Minute == DSeg -> AlarmMinute &&
  2928.            DSeg -> CurrentTime . Second == DSeg -> AlarmSecond &&
  2929.            DSeg -> Alarm)
  2930.             Ring(TRUE);
  2931.  
  2932.             /* If needed, decrement the tea timer. */
  2933.  
  2934.         if(DSeg -> Countdown > 0)
  2935.         {
  2936.             if(!(--DSeg -> Countdown))
  2937.                 Ring(FALSE);
  2938.         }
  2939.  
  2940.             /* Check the display signal before we
  2941.              * redraw it.
  2942.              */
  2943.  
  2944.         if(SignalSet & SIG_DISPL)
  2945.         {
  2946.             DSeg -> Seconds ^= TRUE;
  2947.  
  2948.             Force = TRUE;
  2949.         }
  2950.  
  2951.             /* We are to tell the time. */
  2952.  
  2953.         if(SignalSet & SIG_SPEECH)
  2954.         {
  2955.                 /* Remove speech process if speech
  2956.                  * has been turned off after it has
  2957.                  * been run once.
  2958.                  */
  2959.  
  2960.             if(!DSeg -> Speech)
  2961.             {
  2962.                 if(SpeechProcess)
  2963.                 {
  2964.                     Signal(SpeechProcess,SIG_CLOSE);
  2965.  
  2966.                     Wait(SIG_SHAKE);
  2967.                 }
  2968.             }
  2969.             else
  2970.             {
  2971.                     /* Try to create the speech process. */
  2972.  
  2973.                 if(!SpeechProcess)
  2974.                 {
  2975.                     Forbid();
  2976.  
  2977.                     if(SpeechProcess = CreateFuncProc("DClock-Speech",5,SpeechServer,4000))
  2978.                         Wait(SIG_SHAKE);
  2979.  
  2980.                     Permit();
  2981.                 }
  2982.  
  2983.                     /* Tell the time. */
  2984.  
  2985.                 if(SpeechProcess)
  2986.                     Signal(SpeechProcess,SIG_CLICK);
  2987.             }
  2988.         }
  2989.  
  2990.             /* Show time and date. */
  2991.  
  2992.         if(DSeg -> Page == 0)
  2993.             ShowTime(TRUE,Force);
  2994.         else
  2995.             ShowTime(FALSE,Force);
  2996.  
  2997.         Force = FALSE;
  2998.  
  2999.             /* Show memory display. */
  3000.  
  3001.         if(DSeg -> Page == 1)
  3002.         {
  3003.             LONG BarLength;
  3004.  
  3005.                 /* For single bitplane Workbench
  3006.                  * screen -> use fill pattern.
  3007.                  */
  3008.  
  3009.             static USHORT Checkers[8]=
  3010.             {
  3011.                 0xAAAA,0x5555,
  3012.                 0xAAAA,0x5555,
  3013.                 0xAAAA,0x5555,
  3014.                 0xAAAA,0x5555
  3015.             };
  3016.  
  3017.                 /* How much memory is still available? */
  3018.  
  3019.             MemSize = AvailMem(MEMF_CHIP) + AvailMem(MEMF_FAST);
  3020.  
  3021.                 /* How LONG will the bar be? */
  3022.  
  3023.             BarLength = ((Width - (EmptyWidth + FullWidth)) * MemSize) / MaxSize;
  3024.  
  3025.             if(NewKick)
  3026.             {
  3027.                 SetAPen(DummyRPort,2);
  3028.                 SetBPen(DummyRPort,3);
  3029.             }
  3030.             else
  3031.             {
  3032.                 SetAPen(DummyRPort,1);
  3033.                 SetBPen(DummyRPort,2);
  3034.             }
  3035.  
  3036.             Move(DummyRPort,0,Window -> IFont -> tf_Baseline);
  3037.  
  3038.             Text(DummyRPort,"E",1);
  3039.  
  3040.             Move(DummyRPort,Width - FullWidth,Window -> IFont -> tf_Baseline);
  3041.  
  3042.             Text(DummyRPort,"F",1);
  3043.  
  3044.                 /* Draw the full part. */
  3045.  
  3046.             if(Window -> RPort -> BitMap -> Depth < 2)
  3047.                 SetAfPt(DummyRPort,&Checkers[0],1);
  3048.  
  3049.             if(NewKick)
  3050.                 SetAPen(DummyRPort,2);
  3051.             else
  3052.                 SetAPen(DummyRPort,3);
  3053.  
  3054.             RectFill(DummyRPort,EmptyWidth,0,Width - FullWidth - BarLength,Height - 1);
  3055.  
  3056.             if(Window -> RPort -> BitMap -> Depth < 2)
  3057.                 SetAfPt(DummyRPort,NULL,0);
  3058.  
  3059.                 /* Add the empty part. */
  3060.  
  3061.             if(NewKick)
  3062.                 SetAPen(DummyRPort,3);
  3063.             else
  3064.                 SetAPen(DummyRPort,2);
  3065.  
  3066.             RectFill(DummyRPort,Width - FullWidth - BarLength,0,Width - (FullWidth + 1),Height - 1);
  3067.         }
  3068.  
  3069.             /* Numeric memory display. */
  3070.  
  3071.         if(DSeg -> Page == 2)
  3072.         {
  3073.             UBYTE TempBuff[30];
  3074.  
  3075.             Format(TempBuff,"C: %07ld  F: %07ld",AvailMem(MEMF_CHIP),AvailMem(MEMF_FAST));
  3076.             PrintIt(TempBuff);
  3077.         }
  3078.  
  3079.             /* Show online timer? */
  3080.  
  3081.         if(DSeg -> Page == 3)
  3082.         {
  3083.             UBYTE TempBuff[50];
  3084.  
  3085.             Format(TempBuff,"OFFLINE %02ld:%02ld:%02ld",OnlineHours,OnlineMinutes,OnlineSeconds);
  3086.  
  3087.             if(DSeg -> Online)
  3088.             {
  3089.                 if(Blink ^= TRUE)
  3090.                 {
  3091.                     TempBuff[0] = ' ';
  3092.                     TempBuff[1] = 'O';
  3093.                     TempBuff[2] = 'N';
  3094.                 }
  3095.                 else
  3096.                 {
  3097.                     TempBuff[0] = ' ';
  3098.                     TempBuff[1] = ' ';
  3099.                     TempBuff[2] = ' ';
  3100.                 }
  3101.             }
  3102.  
  3103.             PrintIt(TempBuff);
  3104.         }
  3105.  
  3106.             /* Show the countdown. */
  3107.  
  3108.         if(DSeg -> Page == 4)
  3109.         {
  3110.             UBYTE TempBuff[50];
  3111.  
  3112.             if(DSeg -> Countdown > 0)
  3113.             {
  3114.                 Format(TempBuff,"Countdown %ld         ",-DSeg -> Countdown);
  3115.                 TempBuff[22] = 0;
  3116.  
  3117.                 PrintIt(TempBuff);
  3118.             }
  3119.             else
  3120.                 DSeg -> Page = 0;
  3121.         }
  3122.  
  3123.             /* Transfer the image portion. */
  3124.  
  3125.         ClipBlit(DummyRPort,0,0,Window -> WScreen -> BarLayer -> rp,LeftEdge,1,Width,Height,0xC0);
  3126.     }
  3127. }
  3128.